ES5电子教程
中文版:http://yanhaijing.com/es5/#58
英文版:https://people-mozilla.org/~jorendorff/es5.1-final.html
ES6电子教程
中文版:http://es6.ruanyifeng.com/
英文版:http://www.ecma-international.org/ecma-262/6.0/#sec-type
ES6的浏览器兼容性问题: https://segmentfault.com/a/1190000005128101
ES7电子教程
英文版:http://www.ecma-international.org/ecma-262/7.0/index.html
名词解析
(1)链接: http://www.jb51.net/article/30719.htm
- 变量提升
1、查找变量的顺序是:活动对象-->全局对象,一旦找到变量就停止查找
2、javascrip函数级作用域的影响,即函数会创建新的作用域
3、变量提升,是把变量提升提到函数的顶部,只提升变量的声明,赋值没提升。
//场景一
var x = 1; //x存储在全局对象中
console.log(x); // 1
if (true) {
var x = 2; //x存储在活动对象中
console.log(x); //2
}
console.log(x);// 2
//场景一
var v='Hello World';
(function(){
alert(v);
var v='I love you';
alert(v);
})();
//定义的三个变量
(function(){
var a='One';
var b='Two';
var c='Three';
})();
//实际上是这样子的:
(function(){
var a,b,c;
a='One';
b='Two';
c='Three';
})();
所以在定义js变量时候,需要把变量放在块级作用域的顶端,先定义变量,以防出现错误。
- 函数提升
1、函数提升:就是把函数声明提到作用域的顶部
2、函数有两种表达方式:函数声明方式和函数表达方式
3、只有函数声明方式才能提升
//函数声明方式
function myTest(){
foo();
function foo(){
alert("我来自 foo");
}
}
myTest();
//函数表达方式
function myTest(){
foo();
var foo =function foo(){
alert("我来自 foo");
}
}
myTest();
- ** 暂时性死区**
只要一进入当前作用域,所要使用的变量就已经存在,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。- ** let与var区别**
//不同之处
1、let声明的变量只在let所在的代码块内有效
2、var有变量提升,则声明之前使用不会报错;let没有变量提升,声明前使用会报错
3、let存在暂时性死区,只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响
var tmp = 123;
if (true) {
tmp = 'abc'; // ReferenceError
let tmp;
}
4、ES6规定,若区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡在声明前就使用这些变量,均报错。
5、ES6规定暂时性死区和let、const语句不出现变量提升,主要是为了减少运行时错误。
6、let不允许在相同作用域内,重复声明同一个变量
- ** 块级作用域**
ES5只有全局作用域和函数作用域,没有块级作用域,这带来很多不合理的场景
1、避免由于变量提升,内层变量可能会覆盖外层变量;
2、避免用来计数的循环变量泄露为全局变量;
3、ES6允许块级作用域的任意嵌套, 外层作用域无法读取内层作用域的变量, 内层作用域可以定义外层作用域的同名变量;
{{{{{let insane = 'Hello World'}}}}};
{{{{ {let insane = 'Hello World'} console.log(insane); // 报错}}}};
{{{{ let insane = 'Hello World'; {let insane = 'Hello World'}}}}};
4、块级作用域可以替代立即执行的匿名函数的功能
5、ES5(严格模式)规定,函数只能在顶层作用域和函数作用域之中声明,不能在块级作用域声明
6、ES6允许在块级作用域之中声明函数,只在大括号情况下可行,没有大括号会报错;函数声明类似于var,存在函数提升
注意:环境导致的行为差异太大,应该避免在块级作用域内声明函数。 若需要,也应该写成函数表达式,而不是函数声明语句
- ** 顶层对象及其属性**
根据不同环境,顶层对象指:
1、浏览器:window、self对象。有实体含义,即指代浏览器的窗口对象
2、node: global对象
ES5中,顶层对象的属性与全局变量等价,这是javascript语言设计的最大败笔之一,原因:
1、没法在编译时就报出变量未声明的错误,只有运行时才能知道。因为全局变量可能是顶层对象的属性创造的,而属性的创造是动态的。
2、顶层对象到处可读,不利于模块化编程
3、人为创造全局变量
ES6中,对顶层对象及其属性做了以下处理:
1、保留var和function命令,其定义的全局变量依然为顶层对象的属性
2、let、const、import、class四种命令定义的全局变量不属于顶层对象的属性
使用this对象获取顶层对象,但具有一定局限性:
1、全局环境中,this会返回顶层对象。
但是,Node模块和ES6模块中,this返回的是当前模块。
2、函数里面的this,如果函数不是作为对象的方法运行,而是单纯作为函数运行,this会指向顶层对象。
但是,严格模式下,这时this会返回undefined。
3、不管是严格模式,还是普通模式,new Function('return this')(),总是会返回全局对象。
但是,如果浏览器用了CSP(Content Security Policy,内容安全政策),那么eval、new Function这些方法都可能无法使用
在所有情况下,都取到顶层对象的方法:
//方法一:
(typeof window !== 'undefined'
? window
: (typeof process === 'object' &&
typeof require === 'function' &&
typeof global === 'object')
? global
: this);
//方法二:
var getGlobal = function () {
if (typeof self !== 'undefined') { return self; }
if (typeof window !== 'undefined') { return window; }
if (typeof global !== 'undefined') { return global; }
throw new Error('unable to locate global object');
};
- 解构(Destructuring)赋值
从数组或对象中提取值,按照对应位置,对变量赋值
解构赋值类型:
1、数组形式:数据结构具有Iterator接口.
var: var [v1, v2, ..., vN ] = array;
let: let [v1, v2, ..., vN ] = array;
const: const [v1, v2, ..., vN ] = array;
Set结构: let [x, y, z] = new Set(["a", "b", "c"]);
function* fibs() {
var a = 0;
var b = 1;
while (true) {
yield a;
[a, b] = [b, a + b];
}
}
var [first, second, third, fourth, fifth, sixth] = fibs();
// fibs是一个Generator函数,原生具有Iterator接口。解构赋值会依次从这个接口获取值
2、对象形式:对象解构赋值与数组解构赋值的区别:
* 数组的元素按次序排序,变量的值与位置有关;左边变量在右边的值可以不赋值
* 对象的解构赋值,右边的以字段(变量名与变量值)形式存在,与左边变量名的顺序无关; 左边的变量与右边的变量一直,不可缺少
var { foo, bar } = { foo: "aaa", bar: "bbb" };
foo // "aaa"
bar // "bbb"
var { baz } = { foo: "aaa", bar: "bbb" };
baz // undefined
- 模式匹配
“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。
var [a,b,c]=[1,2,3]; //模式匹配法_变量的批量赋值
console.log(b);
//嵌套数组解构
let [foo,[[fa],fb],fc] = [1,[[2],3],4];
console.log(fa)
let [one,,three] = [1,2,"123"];
console.log(three)
let [start, ...d]=["999",2,"56","00"];
console.log(start)
let [x, y, ...z] = ['a'];
console.log(x)
console.log(y)
console.log(z)
let [...arr] = [];
console.log(arr) //数组
如果解构不成功,变量的值就等于undefined var [fo] = []; // fo undefined
var [bar, fo] = [1]; // fo undefined
// 如果右边不是数组,或者严格地说,不是可遍历的结构,那么会报错:
let [foo] = 1;
let [foo] = false;
let [foo] = NaN;
let [foo] = undefined;
let [foo] = null;
let [foo] = {};
ES5与ES6区别
- 变量声明方式
ES5:2种方式。var, function
ES6:6种方式。var, function,let, const, import, class
- 默认值
ES6允许变量指定默认值。
var [foo = true] = [];
foo // true
[x, y = 'b'] = ['a']; // x='a', y='b'
[x, y = 'b'] = ['a', undefined]; // x='a', y='b'
ES6内部使用严格相等运算符(===),判断一个位置是否有值。所以,如果一个数组成员不严格等于undefined,默认值是不会生效的
var [x = 1] = [undefined];
x // 1
var [x = 1] = [null];
x // null
上面代码中,如果一个数组成员是null,默认值就不会生效,因为null不严格等于undefined
惰性求值: 如果默认值是一个表达式,那么这个表达式是惰性求值的,即只有在用到的时候,才会求值 默认值可以引用解构赋值的其他变量,但是该变量必须已经声明,否则会报错。