目录
1.ES6的兼容性
ES6在各大浏览器兼容性查询ECMAScript 6 compatibility tablehttp://kangax.github.io/compat-table/es6/
1-1 Babel.js
Babel是一个JavaScript转移器。它将使用了ES语言特性的JS代码转换成只使用广泛支持的ES5特性的等价代码。
Babel可以在项目中使用,也可以再浏览器查看转换效果。
2.ES6新功能
2-1 let与var
2-1-1 let关键字
ES6之前,我们可以在代码中重写已声明的变量。
var a = 5;
var a = 7;
console.log(a)
ES6引入的let与const禁止在同一作用域声明已经声明过的变量
let a = 5;
let a = 6;
console.log(a)
2-1-2 const关键字
const与let一样,唯一区别是const定义的变量是只读的,也就是常量。
非对象类型的变量,我们无法改变它的值。
但是对象变量,const允许我们修改或重新赋值对象的属性。但是不能对这个变量重新赋值(因为改变了变量本身的应用,即内存中的引用地址)。
const a = {b:'123'};
a.b = 'abc';
console.log(a) // {b: 'abc'}
以上无报错。
const a = {b:'123'};
a = {c:'123'};
console.log(a)
为什么修改属性没有报错呢?
先看看js中变量的存储关系
JS中变量的存储 - Embrace_LY - 博客园 (cnblogs.com)https://www.cnblogs.com/embrace-ly/p/10659970.html
可以看到对象变量在栈中存储的是指向堆空间的指针,所以当我们改变堆空间的内存时,并不会改变栈变量的指针,所以const的对象属性可以被修改,而当我们重新定义const的对象变量的值时,会为其重新分配堆空间,从而导致栈中的指针变更
2-1-3 let const作用域
function ddd(){
let aaa = '我在外面'; // {1}
if (true) {
let aaa = '我在里面'; // {2}
aaa = '我在里面呢' // {3}
}
console.log(aaa) // {4}
}
ddd(); // 我在外面
执行了ddd函数,在这个函数里,我们声明了一个拥有函数作用域的变量aaa, 又声明了一个在if作用域里的aaa。
我们在if作用域里改变了aaa的值,由于在if作用域里,所以我们改变的是行{2}的变量aaa。而行{4}输出的是函数作用域的aaa。
function ddd(){
let aaa = '我在外面';
if (true) {
aaa = '我在里面呢'
}
console.log(aaa)
}
ddd(); // 我在里面呢
if作用域里,找不到当前作用域的aaa变量,于是便向上找,找到函数作用域的aaa并进行修改。
2-2 模板字面量
用``包裹起来的字面量。
const name = '李秀成';
const words = `我的名字叫${name}`
2-3 箭头函数
简洁代码,并解决了匿名函数的this指向问题,有利于封装回调函数。
2-3-1 ES6箭头函数的this指向?
箭头函数体内的this对象,就是定义该函数时所在的作用域指向的对象,而不是使用时所在的作用域指向的对象。
普通函数下:
var name = 'window'; // 其实是window.name = 'window'
var A = {
name: 'A',
sayHello: function(){
console.log(this.name)
}
}
A.sayHello();// 输出A
var B = {
name: 'B'
}
A.sayHello.call(B);//输出B
A.sayHello.call();//不传参数指向全局window对象,输出window.name也就是window
从上面可以看到,sayHello这个方法是定义在A对象中的,当当我们使用call方法,把其指向B对象,最后输出了B;可以得出,sayHello的this只跟使用时的对象有关
var name = 'window';
var A = {
name: 'A',
sayHello: () => {
console.log(this.name)
}
}
A.sayHello();// 还是以为输出A ? 错啦,其实输出的是window
前文提到this永远指向”该函数所在的作用域指向的对象”,而A只是个对象,并不提供任何作用域环境,那么sayHello所处的是全局作用域(最外层的js环境),指向的对象是winodw对象。所以this指向了window。
用call改变指向呢?
var b = {name:'B'}
A.sayHello.call(b) // window
可以看到,call改变不了箭头函数的this指向
var name = 'window';
function A () {
this.name = 'A';
this.sayHello = () => {
console.log(this.name)
}
}
var aaaaaa = new A()
aaaaaa.sayHello() // A
此时箭头函数指向当前作用域(A函数作用域)
或者:
var name = 'window';
var A = {
name: 'A',
sayHello: function(){
var s = () => console.log(this.name)
return s//返回箭头函数s
}
}
var sayHello = A.sayHello();
sayHello();// 输出A
var B = {
name: 'B';
}
sayHello.call(B); //还是A
sayHello.call(); //还是A
这样就做到了永远指向A对象了,我们再根据“该函数所在的作用域指向的对象”来分析一下:
- 该函数所在的作用域:箭头函数s 所在的作用域是sayHello,因为sayHello是一个函数。
- 作用域指向的对象:A.sayHello指向的对象是A。
所以箭头函数s 中this就是指向A啦 ~~
最后是使用箭头函数其他几点需要注意的地方:
不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
不可以使用yield命令,因此箭头函数不能用作 Generator 函数。
2-4 函数默认值
ES6开始可以自定义函数默认值
function sum(x =1, y=2, z=3) {
return x + y +z
}
console.log(sum(4,2)) // 输出9(4+2+3=9)
Es6之前
function sum(x,y,z) {
if (x === undefined) x =1;
if (y === undefined) y =2;
if (z === undefined) z =3;
return x + y +z
}
当然了,有没有高级点的写法?
function sum() {
var x = (arguments.length>0 && argument[0] !== undefined) ? argument[0] : 1;
var y = (arguments.length>1 && argument[1] !== undefined) ? argument[1] : 2;
var z = (arguments.length>2 && argument[2] !== undefined) ? argument[2] : 3;
return x + y +z
}
2-4-1 argument对象
arguments对象是所有(非箭头)函数中都可用的局部变量。
此对象包含传递给函数的每个参数的条目,第一个条目的索引从0开始。
即使不知道参数名称,我们也可以动态获取并使用这些参数。
function sum() {
console.log(arguments)
}
sum(1,2,3) // {'0':1,'1':2,'2':3}
function sum() {
console.log(arguments[0]);
console.log(arguments[1]);
console.log(arguments[2]);
}
sum(1,2,3)
甚至我们可以修改传参
function sum(x,y,z) {
arguments[1] = 10;
console.log(arguments[0]);
console.log(arguments[1]);
console.log(arguments[2]);
return x+y+z
}
console.log(sum(1,2,3))
2-4-2 argument对象是不是一个数组
可以看到argument和数组一样类型是object
function sum(x,y,z) {
console.log(typeof [1,2,3])
console.log(typeof arguments)
}
sum(1,2,3)
arguments对象不是一个 Array 。它类似于Array,但除了length属性和索引元素之外没有任何Array属性。例如,它没有 pop 方法。但是它可以被转换为一个真正的Array。
为了方便对参数的调用,我们会将arguments转换成数组:
var args = Array.prototype.slice.call(arguments);
var args = [].slice.call(arguments);
// ES2015
const args = Array.from(arguments);
const args = [...arguments];
解析:
Array.prototype.slice();
1 对原数组进行剪切返回一个新的数组对象,
2 这一对象是一个由 begin 和 end 决定的原数组的浅拷贝(包括 begin,不包括end)。
3 原始数组不会被改变。
arr.slice([begin[, end]];
起始索引和终止索引可以留空;
const animals = ['ant', 'bison', 'camel', 'duck', 'elephant'];
console.log(animals.slice(2));
// expected output: Array ["camel", "duck", "elephant"]
console.log(animals.slice(2, 4));
// expected output: Array ["camel", "duck"]
这里使用 Array.prototype.slice().call把arguments进行转换
遇到的疑问,arguments对象不是数组,为什么可以用数组的splice方法进行转换?如果我们将arguments的类似数据进行测试:
Array.prototype.splice.call({ '0': 1, '1': 2, '2': 3 }); // retuurn []
开始对比arguments对象与数组对象的相同点,之后发两者都有一个length属性,并且属性名都是从0-N,将上面用到的arguments对象修改为:{ ‘0’: 1, ‘1’: 2, ‘2’: 3 ,length:3},以及测试过了,如果给该对象属性名修改为其他,则不会转换成功。
Array.prototype.splice.call({ '0': 1, '1': 2, '2': 3 ,length:3},0); // retuurn [ 1, 2, 3 ]
对参数使用slice会阻止某些JavaScript引擎中的优化 (比如 V8 - 更多信息)。如果你关心性能,尝试通过遍历arguments对象来构造一个新的数组。
function reset1() {
for (var i = 0, len = arguments.length, reset = Array(len); i < len; i++) {
reset[i] = arguments[i]
}
return reset;
}
如果调用的参数多于正式声明接受的参数,则可以使用arguments对象。这种技术对于可以传递可变数量的参数的函数很有用。使用 arguments.length来确定传递给函数参数的个数,然后使用arguments对象来处理每个参数。
2-4-3 arguments应用
ES5中,我们可以使用apply在修改this指向的同时,还能够用数组传入参数。
var arr = [1,2,3]
function sum(x,y,z) {
return x + y + z;
}
sum.apply(sum,arr) // 6
sum.apply(undefined,arr) // 6
ES6中,已经有了展开运算符,我们可以更方便的传入函数
sum(...arr)
在函数中 展开运算符...也可以代替arguments,当做剩余参数使用
function sum(x,y,...a){
return x + y + a.length
}
console.log(sum(1,2,"hi",true,888)) //6
ES5写法:
function sum2 (x,y) {
var a = Array.prototype.splice.call(arguments,2);
return x + y + a.length
}
console.log(sum(1,2,"hi",true,888)) // 6
2-4-4 arguments.callee属性
指向当前执行的函数。
function test(){
var fun = arguments.callee // return test函数
}