[js基础]ECMAScript2015(ES6)精要知识点(上)

[js基础]ECMAScript2015(ES6)精要知识点(上),第1张

1.ES6的兼容性

ES6在各大浏览器兼容性查询ECMAScript 6 compatibility tablehttp://kangax.github.io/compat-table/es6/

1-1 Babel.js

Babel是一个JavaScript转移器。它将使用了ES语言特性的JS代码转换成只使用广泛支持的ES5特性的等价代码。

Babel可以在项目中使用,也可以再浏览器查看转换效果。

Babel · The compiler for next generation JavaScriptThe compiler for next generation JavaScripthttps://babeljs.io/repl

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函数
}

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1322315.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-12
下一篇 2022-06-12

发表评论

登录后才能评论

评论列表(0条)

保存