一、变量

  在JavaScript中就用一个变量名表示变量,变量名是大小写英文、数字、$_的组合,不能用数字开头。变量名也不能是JavaScript的关键字;

1、变量的声明

  (1)var:申明一个变量,用=对变量进行赋值。可以把任意数据类型赋值给变量,同一个变量可以反复赋值,而且可以是不同类型的变量,但是要注意只能用var申明一次;

   ES6中新增let和const

  (2)let:所声明的变量,只在let命令所在的代码块内有效;必须先声明,不存在变量提升;不允许在相同作用域内,重复声明同一个变量;

  (3)const声明一个只读的常量。一旦声明,常量的值就不能改变;只声明不赋值,就会报错。

2、变量作用域

  (1)全局变量:声明在函数外部的变量;浏览器环境中全局变量为window属性;

  (2)函数作用域:针对局部变量来说的,在函数中定义的变量在函数外不能获取;

  (3)块级作用域(ES6):

二、数据类型

  6种基本类型

  • 数值(number):整数和小数(比如13.14
  • 字符串(string):文本(比如Hello World)。
  • 布尔值(boolean):表示真伪的两个特殊值,即true(真)和false(假)
  • undefined:表示“未定义”或不存在,即由于目前没有定义,所以此处暂时没有任何值
  • null:表示空值,即此处的值为空。
  • 对象(object):各种值组成的集合。

<原始类型>

1、字符串(string):

  1.1  字符串

  • 字符串是以单引号'或双引号"括起来的任意文本
  • ASCII字符可以以\x##形式的十六进制表示
  • JavaScript 允许采用\uxxxx形式表示一个字符,其中xxxx表示字符的 Unicode 码点。(常用于字符图标)

  1.2 字符串常用方法及属性

  (1)获取字符长度:.length

  (2)获取第n位字符  .charAt(n)

  (3)获取第n位字符编码 .charCodeAt(n)

  (4)拼接字符串  a.concat('123','abc')

  (5)获取、搜索字符串位置方法 str.indexOf('a')   str.lastIndexOf('a')    找不到时返回-1

  (6)删除字符串前后的空格 str.trim()

  (7)截取字符串操作方法

    •  str.slice(start,end);           两个参数可正可负,负值代表从右截取,返回值:[start,end] 也就是说返回从start到end-1的字符
    •  str.substring(start,end);  两个参数都为正数,返回值:[start,end) 也就是说返回从start到end-1的字符
    •  str.substr(start,length);     第一个参数指定子字符串开始位置,第二个参数表示返回的字符个数

  (8)字符串大小写转换方法

    • str.toLowerCase();
    • str.toUpperCase();

  (9)字符串分割成字符串数组   str.split(separator,limit); separator指定字符串或正则,limit指定数组的最大长度 ;str.split("");  每个字符都被分割 ;数组变成字符串arr.join('-')

  (10)字符串模式匹配方法

    • str.match(rgExp);  
    • str.test(rgExp);    

  (11)替换第一个子字符串  str.replace("at","one")

  1.3字符串模板语法  

  反引号(``)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量;

  模板字符串中嵌入变量,需要将变量名写在${}之中

2、数值(Number)

  2.1 数值 

  • JavaScript不区分整数和浮点数,统一用Number表示 ;
  • 在JavaScript的内部采用IEEE754格式来表示数字,所以不区分整数和浮点数,都是用64位浮点数的形式储存。就是说,在JavaScript内部,就根本没有小数。但是有些运算必须得需要整数完成,所以JavaScript有时会把64位的浮点数转换成32位的整数,再进行运算。  
123; // 整数123
0.456; // 浮点数0.456
1.2345e3; // 科学计数法表示1.2345x1000,等同于1234.5
-99; // 负数
NaN; // NaN表示Not a Number,当无法计算结果时用NaN表示
Infinity; // Infinity表示无限大,当数值超过了JavaScript的Number所能表示的最大值时,就表示为Infinity

  2.2 表示方法

   2.2.1 整数
  • 二进制:有前缀0b的数值,出现0,1以外的数字会报错
  • 八进制:有前缀0o的数值,或者是以0后面再跟一个数字(0-7)。如果超出了前面所述的数值范围,则会忽略第一个数字0,视为十进制数
  •     注意:八进制字面量在严格模式下是无效的,会导致支持该模式的JavaScript引擎抛出错误
  • 十六进制:有前缀0x,后跟任何十六进制数字(0~9及A~F),字母大小写都可以,超出范围会报错
  • 十进制

  ES6 提供了二进制和八进制数值的新的写法,分别用前缀0b(或0B)和0o(或0O)表示

0b111110111 === 503 // true
0o767 === 503 // true
  2.2.2 浮点数

  所谓浮点数,就是该数值中必须包含一个小数点,并且小数点后面必须至少有一位数字。与整数不同,浮点数只能用十进制来表示

  浮点数的精度远远不如整数,所以设计浮点数的运算好比较要小心

console.log(0.1 + 0.2 == 0.3); //false 0.30000000000000004
console.log(0.6/0.2); //2.9999999999999996
  2.2.3科学计数法

  对于那些极大极小的数值,可以用e表示法(即科学计数法)表示的浮点数值表示。用e表示法表示的数值等于e前面的数值乘以10的指数次幂

  以下两种情况,JavaScript会自动将数值转为科学计数法表示,其他情况都采用字面形式直接表示。

  • 小数点前的数字多余21位
 console.log(1234567890123456789012);// 1.2345678901234568e+21
console.log(123456789012365648787); //123456789012365660000
  • 小数点后面的0多余5位
console.log(0.0000006);//6e-7
console.log(0.000006); //0.000006

  2.3 范围 

  • 由于内存的限制,ECMAScript并不能保存世界上所有的数值,所以就有了最大值和最小值
  • 最小值保存在Number.MIN_VALUE中,这个值是5e-324
  • 最大值保存在Number.MAX_VALUE,这个值是1.7976931348623157e+308
console.log(Number.MIN_VALUE) //5e-324
console.log(Number.MAX_VALUE); //1.7976931348623157e+308
  • 如果数字超过最大值,javascript会返回Infinity,这称为正向溢出(overflow);
  • 如果等于或超过最小负值-1023(即非常接近0),javascript会直接把这个数转为0,这称为负向溢出(underflow)
  • 如果要想确定一个数值是不是有穷的,可以使用isFinite()函数。这个函数在参数位于最小与最大数值之间时会返回true
var result = Number.MAX_VALUE + Number.MAX_VALUE;
console.log(isFinite(result)); //false

  2.4 常见问题

  • typeof NaN // 'number'  ---NaN不是独立的数据类型,而是一个特殊数值,它的数据类型依然属于Number
  • NaN === NaN // false   ---NaN不等于任何值,包括它本身
  • (1 / +0) === (1 / -0) // false  ---除以正零得到+Infinity,除以负零得到-Infinity,这两者是不相等的

  2.4 数值常用方法

  parseInt('123',10)  用于将字符串转为整数,方法还可以接受第二个参数(2到36之间),表示被解析的值的进制

  parseFloat('314e-2') 用于将一个字符串转为浮点数。

  isNaN(123)      用于方法可以用来判断一个值是否为NaN

3、布尔值(boolean)

  (1)布尔值代表“真”和“假”两个状态。“真”用关键字true表示,“假”用关键字false表示。布尔值只有这两个值。

  (2)以下运算返回布尔值:

    •   两元逻辑运算符: && (And),|| (Or)
    •   前置逻辑运算符: ! (Not)
    •   相等运算符:===!====!=
    •   比较运算符:>>=<<=

  (3)下面六个值被转为false,其他值都视为true

    •   undefined  
    •   null  
    •   false  
    •   0  
    •   NaN  
    •   ""''(空字符串)

<合成类型>

4、对象(object)

  4.1对象基本概念

  • 合成类型(complex type)
  • 对象(object)是 JavaScript 语言的核心概念,也是最重要的数据类型;
  • 对象就是一组“键值对”(key-value)的集合,是一种无序的复合数据集合;
  • JavaScript 规定,如果行首是大括号,一律解释为语句(即代码块)。如果要解释为表达式(即对象),必须在大括号前加上圆括号。

  4.2键名

  • 对象的所有键名都是字符串(ES6 又引入了 Symbol 值也可以作为键值),所以加不加引号都可以;
  • 如果键名不符合标识名的条件(比如第一个字符为数字,或者含有空格或运算符),且也不是数字,则必须加上引号,否则会报错;
  • 对象的属性之间用逗号分隔,最后一个属性后面可以加逗号(trailing comma),也可以不加;

  4.3对象的引用

  如果不同的变量名指向同一个对象,那么它们都是这个对象的引用,也就是说指向同一个内存地址。修改其中一个变量,会影响到其他所有变量。

  4.4对象的方法

  • Object.keys查看一个对象本身的所有属性
  • delete命令用于删除对象的属性,删除成功后返回true;只有一种情况,delete命令会返回false,那就是该属性存在,且不得删除(Object.defineProperty);delete命令只能删除对象本身的属性,无法删除继承的属性
  • in运算符用于检查对象是否包含某个属性(注意,检查的是键名,不是键值),如果包含就返回true,否则返回false;in运算符的一个问题是,它不能识别哪些属性是对象自身的,哪些属性是继承的。
  • for...in循环用来遍历一个对象的全部属性
    • 它遍历的是对象所有可遍历(enumerable)的属性,会跳过不可遍历的属性。
    • 它不仅遍历对象自身的属性,还遍历继承的属性。
    • hasOwnProperty方法,在循环内部判断一下,某个属性是否为对象自身的属性。
  • with语句用于操作同一个对象的多个属性时,提供一些书写的方便
var obj = {p1: 1, p2: 2,};
with (obj) {
p1 = 4;
p2 = 5;
}

5、数组(object)

  5.1 数组的基本概念

  • 数组(array)是按次序排列的一组值。每个值的位置都有编号(从0开始),整个数组用方括号表示;
  • 本质上,数组属于一种特殊的对象。typeof运算符会返回数组的类型是object;
  • 当数组的某个位置是空元素,即两个逗号之间没有任何值,我们称该数组存在空位(hole)   ;
  • 数组的空位是可以读取的,返回undefined;
  • 数组的某个位置是空位,与某个位置是undefined,是不一样的。如果是空位,使用数组的forEach方法、for...in结构、以及Object.keys方法进行遍历,空位都会被跳过

  5.2 数组的构造方法

   Array是 JavaScript 的原生对象,同时也是一个构造函数,可以用它生成新的数组。   

var arr = new Array(2);

  Array构造函数对于不同的参数,返回不一样的结果

// 无参数时,返回一个空数组
new Array() // []
// 单个正整数参数,表示返回的新数组的长度
new Array(1) // [ empty ]
// 非正整数的数值作为参数,会报错
new Array(3.2) // RangeError: Invalid array length
new Array(-3) // RangeError: Invalid array length
// 单个非数值(比如字符串、布尔值、对象等)作为参数,
// 则该参数是返回的新数组的成员
new Array('abc') // ['abc']
new Array([1]) // [Array[1]]
// 多参数时,所有参数都是返回的新数组的成员
new Array(1, 2) // [1, 2]
new Array('a', 'b', 'c') // ['a', 'b', 'c']

  

  5.3 数组的length属性

  (1)arr.length 数组的length属性,返回数组的成员数量  

    • JavaScript 使用一个32位整数,保存数组的元素个数。这意味着,数组成员最多只有 4294967295 个(232 - 1)个,也就是说length属性的最大值就是 4294967295
    • 如果最后一个元素后面有逗号,并不会产生空位。也就是说,有没有这个逗号,结果都是一样的
    • delete命令删除一个数组成员,会形成空位,并且不会影响length属性

  (2)length属性是可写的

    • 如果人为设置一个小于当前成员个数的值,该数组的成员会自动减少到length设置的值  ;清空数组的一个有效方法,就是将length属性设为0
    • 如果人为设置length大于当前元素个数,则数组的成员数量会增加到这个值,新增的位置都是空位 ;length属性设为大于数组个数时,读取新增的位置都会返回undefined

  (3)可使用检查某个键名是否存在的运算符in。   

  5.4 数组静态方法Array.isArray()

   Array.isArray方法返回一个布尔值,表示参数是否为数组。它可以弥补typeof运算符的不足

  5.5数组常见方法

  (1)valueOf方法是一个所有对象都拥有的方法,表示对该对象求值;alueOf方法返回数组本身;

  (2)toString方法也是对象的通用方法,数组的toString方法返回数组的字符串形式。

  (3)push方法用于在数组的末端添加一个或多个元素,并返回添加新元素后的数组长度。注意,该方法会改变原数组。

  (4)pop方法用于删除数组的最后一个元素,并返回该元素。注意,该方法会改变原数组。对空数组使用pop方法,不会报错,而是返回undefined

  (5)shift方法用于删除数组的第一个元素,并返回该元素。shift方法可以遍历并清空一个数组。

  (6)unshift方法用于在数组的第一个位置添加元素,并返回添加新元素后的数组长度。注意,该方法会改变原数组。

  (7)join方法以指定参数作为分隔符,将所有数组成员连接为一个字符串返回。如果不提供参数,默认用逗号分隔。如果数组成员是undefinednull或空位,会被转成空字符串。

var a = [1, 2, 3, 4];
a.join(' ') // '1 2 3 4'
a.join(' | ') // "1 | 2 | 3 | 4"
a.join() // "1,2,3,4"

  (8)concat方法用于多个数组的合并。它将新数组的成员,添加到原数组成员的后部,然后返回一个新数组,原数组不变。

['hello'].concat(['world'])
// ["hello", "world"]
['hello'].concat(['world'], ['!'])
// ["hello", "world", "!"]
[].concat({a: 1}, {b: 2})
// [{ a: 1 }, { b: 2 }]
[2].concat({a: 1})
// [2, {a: 1}]

  (9)reverse方法用于颠倒排列数组元素,返回改变后的数组。注意,该方法将改变原数组。

  (10)slice方法用于提取目标数组的一部分,返回一个新数组,原数组不变。arr.slice(start, end);

     slice方法的一个重要应用,是将类似数组的对象转为真正的数组。

Array.prototype.slice.call({ 0: 'a', 1: 'b', length: 2 })
// ['a', 'b']
Array.prototype.slice.call(document.querySelectorAll("div"));
Array.prototype.slice.call(arguments);

  (11)splice方法用于删除原数组的一部分成员,并可以在删除的位置添加新的数组成员,返回值是被删除的元素。注意,该方法会改变原数组。

      arr.splice(start, count, addElement1, addElement2, ...);
var a = ['a', 'b', 'c', 'd', 'e', 'f'];
a.splice(4, 2) // ["e", "f"]
a // ["a", "b", "c", "d"]
var a = ['a', 'b', 'c', 'd', 'e', 'f'];
a.splice(4, 2, 1, 2) // ["e", "f"]
a // ["a", "b", "c", "d", 1, 2]
var a = [1, 1, 1];
a.splice(1, 0, 2) // []
a // [1, 2, 1, 1]
var a = [1, 2, 3, 4];
a.splice(2) // [3, 4]
a // [1, 2] 如果只提供第一个参数,等同于将原数组在指定位置拆分成两个数组

  (12)sort方法对数组成员进行排序,默认是按照字典顺序排序。排序后,原数组将被改变。如果想让sort方法按照自定义方式排序,可以传入一个函数作为参数。

  [ { name: "张三", age: 30 },{ name: "李四", age: 24 },{ name: "王五", age: 28  }].sort(function (o1, o2) {return o1.age - o2.age;})

  (13)map方法将数组的所有成员依次传入参数函数,然后把每一次的执行结果组成一个新数组返回;

  var numbers = [1, 2, 3];
  numbers.map(function (n) {
    return n + 1;
  });// [2, 3, 4]
  numbers// [1, 2, 3]
  • map方法的回调函数有三个参数,elem为当前成员的值,index为当前成员的位置,arr为原数组([1, 2, 3])。
  • map方法还可以接受第二个参数,用来绑定回调函数内部的this变量

  (14)forEach 方法与map方法很相似,也是对数组的所有成员依次执行参数函数。但是,forEach方法不返回值,只用来操作数据。这就是说,如果数组遍历的目的是为了得到返回值,那么使用map方法,否则使用forEach方法。

  • 注意,forEach方法无法中断执行,总是会将所有成员遍历完。如果希望符合某种条件时,就中断遍历,要使用for循环 ;
  •  forEach方法不会跳过undefinednull,但会跳过空位。;
var out = [];
[1, 2, 3].forEach(function(elem) {
this.push(elem * elem);
}, out);
out // [1, 4, 9]

  (15)filter方法用于过滤数组成员,满足条件的成员组成一个新数组返回。

     它的参数是一个函数,所有数组成员依次执行该函数,返回结果为true的成员组成一个新数组返回。该方法不会改变原数组

[1, 2, 3, 4, 5].filter(function (elem) {
return (elem > 3);
})
// [4, 5]

  (16)some方法是只要一个成员的返回值是true,则整个some方法的返回值就是true,否则返回false

var arr = [1, 2, 3, 4, 5];
arr.some(function (elem, index, arr) {
return elem >= 3;
});
// true

  (17)every方法是所有成员的返回值都是true,整个every方法才返回true,否则返回false

var arr = [1, 2, 3, 4, 5];
arr.every(function (elem, index, arr) {
return elem >= 3;
});
// false

  (18)reduce方法和reduceRight方法依次处理数组的每个成员,最终累计为一个值。它们的差别是,reduce是从左到右处理(从第一个成员到最后一个成员),reduceRight则是从右到左(从最后一个成员到第一个成员),其他完全一样。

[1, 2, 3, 4, 5].reduce(function (a, b) {
console.log(a, b);
return a + b;
})
// 1 2
// 3 3
// 6 4
// 10 5
//最后结果:15

  (19)indexOf方法返回给定元素在数组中第一次出现的位置,如果没有出现则返回-1

      indexOf方法还可以接受第二个参数,表示搜索的开始位置。

['a', 'b', 'c'].indexOf('a', 1) // -1

  (20)lastIndexOf方法返回给定元素在数组中最后一次出现的位置,如果没有出现则返回-1

6、函数(Function)

  • 函数是一段可以反复调用的代码块。函数还能接受输入的参数,不同的参数会返回不同的值。
  • JavaScript 语言将函数看作一种值,与其它值(数值、字符串、布尔值等等)地位相同。凡是可以使用值的地方,就能使用函数。比如,可以把函数赋值给变量和对象的属性,也可以当作参数传入其他函数,或者作为函数的结果返回。函数只是一个可以执行的值,此外并无特殊之处。

  6.1函数的声明

  (1)function命令声明的代码区块,就是一个函数。function命令后面是函数名,函数名后面是一对圆括号,里面是传入函数的参数。函数体放在大括号里面。

function print(s) {
console.log(s);
}

  (2)采用变量赋值的写法

var print = function(s) {
console.log(s);
};

  (3)Function构造函数

    Function构造函数接受三个参数,除了最后一个参数是add函数的“函数体”,其他参数都是add函数的参数

var add = new Function(
'x',
'y',
'return x + y'
);

  6.2函数的使用

  (1)调用函数时,要使用圆括号运算符。圆括号之中,可以加入函数的参数。

  (2)函数体内部的return语句,表示返回。JavaScript 引擎遇到return语句,就直接返回return后面的那个表达式的值,后面即使还有语句,也不会得到执行。也就是说,return语句所带的那个表达式,就是函数的返回值。return语句不是必需的,如果没有的话,该函数就不返回任何值,或者说返回undefined

  (3)函数可以调用自身,这就是递归(recursion)。下面就是通过递归,计算斐波那契数列的代码。

function fib(num) {
if (num === 0) return 0;
if (num === 1) return 1;
return fib(num - 2) + fib(num - 1);
}
fib(6) //

  6.3函数与数据类型

  • JavaScript 语言将函数看作一种值,与其它值(数值、字符串、布尔值等等)地位相同。凡是可以使用值的地方,就能使用函数。比如,可以把函数赋值给变量和对象的属性,也可以当作参数传入其他函数,或者作为函数的结果返回。函数只是一个可以执行的值,此外并无特殊之处。
  • 由于函数与其他数据类型地位平等,所以在 JavaScript 语言中又称函数为第一等公民。
function add(x, y) {
return x + y;
}
// 将函数赋值给一个变量
var operator = add;
// 将函数作为参数和返回值
function a(op){
return op;
}
a(add)(1, 1) // 函数柯里化
//

  6.4函数名的提升

  JavaScript 引擎将函数名视同变量名,所以采用function命令声明函数时,整个函数会像变量声明一样,被提升到代码头部。所以,下面的代码不会报错

f();
function f() {}

  表面上,上面代码好像在声明之前就调用了函数f。但是实际上,由于“变量提升”,函数f被提升到了代码头部,也就是在调用之前已经声明了。但是,如果采用赋值语句定义函数,JavaScript 就会报错。

f();
var f = function (){};
// TypeError: undefined is not a function

  6.5不得在非函数的代码块中声明函数,最常见的情况就是iftry语句

if (foo) {
function x() {}
}
try {
function x() {}
} catch(e) {
console.log(e);
}

  6.6函数的属性和方法

  (1)f.name 函数的name属性返回函数的名字

  (2)f.length属性返回函数预期传入的参数个数,即函数定义之中的参数个数

  (3)f.toString方法返回一个字符串,内容是函数的源码

  6.7函数的作用域

  (1)作用域(scope)指的是变量存在的范围。在 ES5 的规范中,Javascript 只有两种作用域:一种是全局作用域,变量在整个程序中一直存在,所有地方都可以读取;另一种是函数作用域,变量只在函数内部存在。

  • 函数外部声明的变量就是全局变量(global variable),它可以在函数内部读取。
  • 在函数内部定义的变量,外部无法读取,称为“局部变量”(local variable)
  • 函数内部定义的变量,会在该作用域内覆盖同名全局变量。

  (2)与全局作用域一样,函数作用域内部也会产生“变量提升”现象。var命令声明的变量,不管在什么位置,变量声明都会被提升到函数体的头部

function foo(x) {
if (x > 100) {
var tmp = x - 100;
}
}
// 等同于
function foo(x) {
var tmp;
if (x > 100) {
tmp = x - 100;
};
}

  (3)函数本身也是一个值,也有自己的作用域。它的作用域与变量一样,就是其声明时所在的作用域,与其运行时所在的作用域无关。

var a = 1;
var x = function () {
console.log(a);
};
function f() {
var a = 2;
x();
}
f() // 1

  函数x是在函数f的外部声明的,所以它的作用域绑定外层,内部变量a不会到函数f体内取值,所以输出1,而不是2

  函数执行时所在的作用域,是定义时的作用域,而不是调用时所在的作用域。

  同样的,函数体内部声明的函数,作用域绑定函数体内部

function foo() {
var x = 1;
function bar() {
console.log(x);
}
return bar;
} var x = 2;
var f = foo();
f() //

  6.8参数

  函数运行的时候,有时需要提供外部数据,不同的外部数据会得到不同的结果,这种外部数据就叫参数。

  (1)函数参数不是必需的,Javascript 允许省略参数。

  (2)如果有同名的参数,则取最后出现的那个值

  (3)arguments 对象

  • 由于 JavaScript 允许函数有不定数目的参数,所以需要一种机制,可以在函数体内部读取所有参数。这就是arguments对象的由来

  • arguments对象包含了函数运行时的所有参数,arguments[0]就是第一个参数,arguments[1]就是第二个参数,以此类推。这个对象只有在函数体内部,才可以使用。
var f = function (one) {
console.log(arguments[0]);
console.log(arguments[1]);
console.log(arguments[2]);
}
f(1, 2, 3)
//
//
//

  正常模式下,arguments对象可以在运行时修改。

  严格模式下,arguments对象是一个只读对象,修改它是无效的,但不会报错。

  通过arguments对象的length属性,可以判断函数调用时到底带几个参数。

  虽然arguments很像数组,但它是一个对象。数组专有的方法(比如sliceforEach),不能在arguments对象上直接使用

  如果要让arguments对象使用数组方法,真正的解决方法是将arguments转为真正的数组。下面是两种常用的转换方法:slice方法和逐一填入新数组。

var args = Array.prototype.slice.call(arguments);

// 或者
var args = [];
for (var i = 0; i < arguments.length; i++) {
args.push(arguments[i]);
}

  arguments对象带有一个callee属性,返回它所对应的原函数。

var f = function () {
console.log(arguments.callee === f);
} f() // true

  6.9闭包

  变量作用域。前面提到,JavaScript 有两种作用域:全局作用域和函数作用域。函数内部可以直接读取全局变量。

var n = 999;
function f1() {
console.log(n);
}
f1() //

  上面代码中,函数f1可以读取全局变量n。但是,函数外部无法读取函数内部声明的变量。

function f1() {
var n = 999;
} console.log(n)

  上面代码中,函数f1内部声明的变量n,函数外是无法读取的

  如果出于种种原因,需要得到函数内的局部变量。正常情况下,这是办不到的,只有通过变通方法才能实现。那就是在函数的内部,再定义一个函数。

function f1() {
var n = 999;
function f2() {
  console.log(n); //
}
}

上面代码中,函数f2就在函数f1内部,这时f1内部的所有局部变量,对f2都是可见的。但是反过来就不行,f2内部的局部变量,对f1就是不可见的。这就是 JavaScript 语言特有的”链式作用域”结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。

既然f2可以读取f1的局部变量,那么只要把f2作为返回值,我们不就可以在f1外部读取它的内部变量了吗  

function f1() {
var n = 999;
function f2() {
console.log(n);
}
return f2;
}
var result = f1();
result(); //

上面代码中,函数f1的返回值就是函数f2,由于f2可以读取f1的内部变量,所以就可以在外部获得f1的内部变量了

闭包的最大用处有两个,一个是可以读取函数内部的变量,另一个就是让这些变量始终保持在内存中,即闭包可以使得它诞生环境一直存在。请看下面的例子,闭包使得内部变量记住上一次调用时的运算结果。

function createIncrementor(start) {
return function () {
return start++;
};
} var inc = createIncrementor(5); inc() //
inc() //
inc() //

上面代码中,start是函数createIncrementor的内部变量。通过闭包,start的状态被保留了,每一次调用都是在上一次调用的基础上进行计算。从中可以看到,闭包inc使得函数createIncrementor的内部环境,一直存在。所以,闭包可以看作是函数内部作用域的一个接口。为什么会这样呢?原因就在于inc始终在内存中,而inc的存在依赖于createIncrementor,因此也始终在内存中,不会在调用结束后,被垃圾回收机制回收。

闭包的另一个用处,是封装对象的私有属性和私有方法

function Person(name) {
var _age;
function setAge(n) {
_age = n;
}
function getAge() {
return _age;
} return {
name: name,
getAge: getAge,
setAge: setAge
};
} var p1 = Person('张三');
p1.setAge(25);
p1.getAge() //

上面代码中,函数Person的内部变量_age,通过闭包getAgesetAge,变成了返回对象p1的私有变量。

注意,外层函数每次运行,都会生成一个新的闭包,而这个闭包又会保留外层函数的内部变量,所以内存消耗很大。因此不能滥用闭包,否则会造成网页的性能问题。

  6.10立即调用的函数表达式

  在 Javascript 中,圆括号()是一种运算符,跟在函数名之后,表示调用该函数。比如,print()就表示调用print函数。

(function(){ /* code */ }());
// 或者
(function(){ /* code */ })();

  上面两种写法都是以圆括号开头,引擎就会认为后面跟的是一个表示式,而不是函数定义语句,所以就避免了错误。这就叫做“立即调用的函数表达式”(Immediately-Invoked Function Expression),简称 IIFE。

  注意,上面两种写法最后的分号都是必须的。如果省略分号,遇到连着两个 IIFE,可能就会报错。

!function () { /* code */ }();
~function () { /* code */ }();
-function () { /* code */ }();
+function () { /* code */ }();

  通常情况下,只对匿名函数使用这种“立即执行的函数表达式”。它的目的有两个:一是不必为函数命名,避免了污染全局变量;二是 IIFE 内部形成了一个单独的作用域,可以封装一些外部无法读取的私有变量。

// 写法一
var tmp = newData;
processData(tmp);
storeData(tmp); // 写法二
(function () {
var tmp = newData;
processData(tmp);
storeData(tmp);
}());

7、null 和 undefined

 7.1 undefined是一个表示”此处无定义”的原始值,转为数值时为NaN

 7.2  null是一个表示“空”的对象,转为数值时为0

if (!undefined) {
console.log('undefined is false');
}
// undefined is false if (!null) {
console.log('null is false');
}
// null is false undefined == null
// true Number(null) //
+ null // Number(undefined) // NaN
+ undefined // NaN

三、数据类型判断

1、typeof

typeof运算符可以返回一个值的数据类型

  • 数值、字符串、布尔值分别返回numberstringboolean
  • 函数返回function
  • undefined返回undefined
  • 对象返回object
typeof 123 // "number"
typeof '123' // "string"
typeof false // "boolean" function f() {}
typeof f // "function" typeof undefined // "undefined" typeof window // "object"
typeof {} // "object"
typeof [] // "object"

2、instanceof

instanceof运算符返回一个布尔值,表示对象是否为某个构造函数的实例

(1)instanceof运算符的左边是实例对象,右边是构造函数。它会检查右边构建函数的原型对象(prototype),是否在左边对象的原型链上。

var v = new Vehicle();
v instanceof Vehicle // true

(2)由于instanceof检查整个原型链,因此同一个实例对象,可能会对多个构造函数都返回true

var d = new Date();
d instanceof Date // true
d instanceof Object // true

(3)instanceof的原理是检查右边构造函数的prototype属性,是否在左边对象的原型链上。有一种特殊情况,就是左边对象的原型链上,只有null对象。这时,instanceof判断会失真。

var obj = Object.create(null);
typeof obj // "object"
Object.create(null) instanceof Object // false

(4)区别array和obj

var x = [1, 2, 3];
var y = {};
x instanceof Array // true
y instanceof Object // true

(5)instanceof运算符只能用于对象,不适用原始类型的值。

var s = 'hello';
s instanceof String // false

(6)对于undefinednullinstanceOf运算符总是返回false

undefined instanceof Object // false
null instanceof Object // false

3、Object.prototype.toString

(1)基本类型判断

console.log(Object.prototype.toString.call("jerry"));//[object String]
console.log(Object.prototype.toString.call(12));//[object Number]
console.log(Object.prototype.toString.call(true));//[object Boolean]
console.log(Object.prototype.toString.call(undefined));//[object Undefined]
console.log(Object.prototype.toString.call(null));//[object Null]

(2)判断原生引用类型

console.log(Object.prototype.toString.call({name: "jerry"}));//[object Object]
console.log(Object.prototype.toString.call(function(){}));//[object Function]
console.log(Object.prototype.toString.call([]));//[object Array]
console.log(Object.prototype.toString.call(new Date));//[object Date]
console.log(Object.prototype.toString.call(/\d/));//[object RegExp]

(3)自定义类型

这种方法不能准确判断personPerson类的实例,而只能用instanceof 操作符来进行判断

function Person(name, age) {
this.name = name;
this.age = age;
}
var person = new Person("Rose", 18);
Object.prototype.toString.call(arr); // "[object Object]"

注:

部分内容引用和参考:JavaScript 标准参考教程

常用js功能函数汇总(持续更新ing)的更多相关文章

  1. MySQL常用SQL/函数汇总(持续更新)

    自动生成ROWNUN SELECT (@rowNO := @rowNo+1) AS rowno,a.uuid FROM (SELECT * FROM h_log_proc) a,(SELECT @ro ...

  2. 收藏的iOS技术站点汇总(持续更新ing)

    大牛博客 objc.io PS:经典,内容深而广 objc中国 NSHipster PS:非常多小细节 NSHipster 中文版 唐巧的技术博客 PS:LZ是唐巧的脑残粉- OneV's Den 王 ...

  3. C++的一些小操作、常用库及函数(持续更新)

    1. 强制保留n位小数(位数不足则强制补零) 头文件: #include <iomanip> 在输出前: cout<<setprecision(n); 也有不用头文件的方式,在 ...

  4. 常用js功能函数集合

    1.获取随机时间戳 function uniqueId(){         var a=Math.random,b=parseInt;         return Number(new Date( ...

  5. 《WCF技术剖析》博文系列汇总[持续更新中]

    原文:<WCF技术剖析>博文系列汇总[持续更新中] 近半年以来,一直忙于我的第一本WCF专著<WCF技术剖析(卷1)>的写作,一直无暇管理自己的Blog.在<WCF技术剖 ...

  6. python3.4学习笔记(六) 常用快捷键使用技巧,持续更新

    python3.4学习笔记(六) 常用快捷键使用技巧,持续更新 安装IDLE后鼠标右键点击*.py 文件,可以看到Edit with IDLE 选择这个可以直接打开编辑器.IDLE默认不能显示行号,使 ...

  7. 中国.NET:各地微软技术俱乐部汇总(持续更新中...)

    中国.NET:各地微软技术俱乐部汇总(持续更新中...)   本文是转载文,源地址: https://www.cnblogs.com/panchun/p/JLBList.html by ​史记微软. ...

  8. redis日常使用汇总--持续更新

    redis日常使用汇总--持续更新 工作中有较多用到redis的场景,尤其是触及性能优化的方面,传统的缓存策略在处理持久化和多服务间数据共享的问题总是不尽人意,此时引入redis,但redis是单线程 ...

  9. Impala系列: Impala常用的功能函数

    --=======================查看内置的函数--=======================hive 不需要进入什么内置数据库, 即可使用 show functions 命令列出 ...

随机推荐

  1. iOS 组件化

    iOS 组件化介绍 随着应用需求逐步迭代,应用的代码体积将会越来越大,为了更好的管理应用工程,我们开始借助CocoaPods版本管理工具对原有应用工程进行拆分.但是仅仅完成代码拆分还不足以解决业务之间 ...

  2. WebService从服务端到客户端的用例

    1.首先编写Wsdl(基于契约优先的方式),要注意的是命名空间(若是使用include或import)最好使用一致的,代码如下: <?xml version="1.0" en ...

  3. (转)CentOS下一键安装GitLab

    [环境准备]OS: CentOS 6.3 x86_64 [安装要求]如果有条件,提供一台全新的Server(仅仅只安装了一些系统的软件包),可以直接使用一键安装脚本(gitlab-install-el ...

  4. Linux云自动化运维第五课

    Linux云自动化运维第五课 一.进程定义 进程就是cpu未完成的工作 二.ps命令 ps a ###关于当前环境的所有进程 x ###与当前环境无关的所有进程 f ###显示进程从属关系 e ### ...

  5. 【原】Linux设备网络硬件管理

    遇到网络问题时候,一般情况下,我们第一反应是查找软件方面问题,但排查之后,软件没有问题的时候,我们就需要排查硬件方面工作是否正常. 我们可能需要查询网卡设备本身的状态,查询网卡是否有数据包发送接收: ...

  6. javaWEB与Session

    HttpSession(*****)1. HttpSession概述  * HttpSession是由JavaWeb提供的,用来会话跟踪的类.session是服务器端对象,保存在服务器端!!!  * ...

  7. require include 一个隐藏的用法:作用域。

    最近在研究php底层框架,奇怪的一点.控制器里为什么要把从model里获取的数据 需要$this->assign('items', $items); 这种形式模板文件里才可以调用到这个变量.控制 ...

  8. centos5.11架设svn(svn系列 架设服务器 知识一总结)

    ♣svn是什么? ♣安装    --yum安装    --创建svn版本库目录    --创建版本库    --进入conf目录进行配置(该svn版本库配置文件)    --启动svn版本库    - ...

  9. Java并发编程:Java线程池

    转载自:http://www.cnblogs.com/dolphin0520/p/3932921.html 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题 ...

  10. 1.Java集合总结系列:Java集合概述

    一.概述 集合是 Java 中非常重要的 API,在实际应用中非常广泛,在许多面试中也是必考的知识点. Java 所有集合类都位于 java.util 包下,Java 的集合只能保存对象,而无法保存保 ...