ECMAScript5、【10bet】ECMAScript6 新增知识点总结

来源:http://www.chinese-glasses.com 作者:Web前端 人气:62 发布时间:2020-04-22
摘要:时间: 2019-09-23阅读: 144标签: 原生 ECMAScript发展历史 (1)ECMA-262第1版:去除了对针对浏览器的特性,支持Unicode标准(多语言开发),对象也变成了和平台无关的。(1997年) (2)ECMA-262 第

时间: 2019-09-23阅读: 144标签: 原生

ECMAScript发展历史

(1)ECMA-262 第1版:去除了对针对浏览器的特性,支持Unicode标准(多语言开发),对象也变成了和平台无关的。(1997年)
(2)ECMA-262 第2版:没有增删改,就是编辑加工工作。(1998年6月)
(3)第三版ECMAScript3新增了对正则表达式、新控制语句、try-catch异常处理的支持,修改了字符处理、错误定义和数值输出等内容。标志着ECMAScript成为了一门真正的编程语言。(1999年12月)
(4)第四版于2008年7月发布前被废弃, 但是它的大部分内容被ES6继承了。
(5)第五版ECMAScript5力求澄清第3版中的歧义,并添加了新的功能。新功能包括:原生JSON对象、继承的方法、高级属性的定义以及引入严格模式。
(6)第六版ECMAScript6是继ES5之后的一次主要改进,增添了许多必要的特性,例如:模块和类以及一些实用特性,Maps、Sets、Promises、生成器(Generators)等。

作为前端开发工程师,盲目追逐框架似乎有点舍本逐末,要知道基本功才是硬核。JavaScript 的语法这几年一直在更新,不管我们是框架的核心开发者还是业务重塑者,学习下最新的 JavaScript 语法和能力是非常有好处的。下面我们通过几个小示例来看下新语法的强大之处:

ECMAScript5部分新增特性简介

“初始化一个数组,要求数组的长度是 5,每个元素的默认值是 0”

一、浏览器支持:

Chrome13+、FireFox4+、Safari 5.1、IE9
IE9不支持严格模式
Safari5.1*不支持function.prototype.bind方法

这道题看似非常简单,我们可能会这样来写代码:

二、JSON对象

JSON.parse(jsonstr) 将json字符串转化成json对象
JSON.stringify(jsonobj) 将json对象转化成json字符串

eval() :
它可以编译执行任何JavaScript程序,因此产生了安全性问题。当使用可信任与完善的源代码时才可以使用eval函数。
json2.js :
https://github.com/douglascrockford/JSON-js/blob/master/json2.js

const arr = []for(let i = 0; i  5; i++){ arr.push(0)}
三、新增Object接口
对象 构造器 说明
Object getPrototypeOf 返回对象的原型
Object GetOwnPropertyDescriptor 返回对象自有属性的属性描述符
Object getOwnPropertyNames 返回一个数组,包括对象所有自有属性名称集合(包括不可枚举的属性)
Object create 创建一个拥有置顶原型和若干个指定属性的对象
Object defineProperty 给对象定义一个新属性,或者修改已有的属性,并返回
Object defineProperties 在一个对象上添加或修改一个或者多个自有属性,并返回该对象
Object seal 锁定对象。阻止修改现有属性的特性,并阻止添加新属性。但是可以修改已有属性的值。
Object freeze 冻结对象,阻止对对象的一切操作。冻结对象将永远不可变。
Object preventExtensions 让一个对象变的不可扩展,也就是永远不能再添加新的属性。
Object isSealed 判断对象是否被锁定
Object isFrozen 判断对象是否被冻结
Object isExtensible 判断对象是否可以被扩展
Object keys 返回一个由给定对象的所有可枚举自身属性的属性名组成的数组

Objec.defineProperty 设置对象属性:
value, writable, enumerable, configurable

Object.create 兼容性处理:

if(!Object.create){
    Object.create = function(proto){
        function F(){};
        F.prototype = proto;
            return new F();
        }
}

缺点:无法继承“父类”自身的属性
经典的继承:

function SuperClass(name){
    this.name = name;
}
SuperClass.prototype.sayName = function(){
    alert(this.name);
}

function SubClass(name,age){
    SuperClass.call(this,name);
    this.age = age;
}
SubClass.prototype = SuperClass.prototype;//错误的 在SubClass.prototype上面添加属性的时候,SuperClass.prototype也会被修改。
SubClass.prototype = new SuperClass(); //不好,不能给SuperClass传参
SubClass.prototype = Object.create(SuperClass.prototype); //原型链写不向上查找的特性
SubClass.prototype.constructor = SubClass;
SubClass.prototype.sayAge = function(){
    alert(this.age);
}

var sub = new SubClass("nie",19);
sub.sayAge();
sub.sayName();

可是如果你学习过 ES6 的语法,就会知道 Array 新增的原型对象方法上有个 fill 的 API,它可以轻松实现这个题目,代码如下:

四、新增Array接口
对象 构造器 说明
Array.prototype indexOf 返回根据给定元素找到的第一个索引值,否则返回-1
Array.prototype lastIndexOf 方法返回指定元素在数组中的最后一个的索引,如果不存在则返回 -1
Array.prototype every 测试数组的所有元素是否都通过了指定函数的测试
Array.prototype some 测试数组中的某些元素是否通过了指定函数的测试
Array.prototype forEach 让数组的每一项都执行一次给定的函数
Array.prototype map 返回一个由原数组中的每个元素调用一个指定方法后的返回值组成的新数组
Array.prototype filter 利用所有通过指定函数测试的元素创建一个新的数组,并返回
Array.prototype reduce 接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终为一个值
Array.prototype reduceRight 接受一个函数作为累加器,让每个值(从右到左,亦即从尾到头)缩减为一个值

另外,还有一个 Array.isArray(),用来判断某一对象是否为数组。(typeof判断的话,返回object,用instanceof判断的话,IE上的返回值不正确)
低版本浏览器判断数组的方法:

function isArray(o) {
    return Object.prototype.toString.call(o) ==="[object Array]";
}
constarr =Array(5).fill(0)
五、Function.prototype.bind

bind()方法会创建一个新函数,称为绑定函数.当调用这个绑定函数时,绑定函数会以创建它时传入bind()方法的第一个参数作为 this,传入 bind() 方法的第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数。
这个方法可以改变this的指向,为函数自定义 this指针。
作用和call,apply类似,区别是 bind将会返回一个新的函数,而 call或者 apply并不会返回一个新的函数,它们将会使用新的 this指针直接进行函数调用

兼容性解决:

if(!Function.prototype.bind){
    Function.prototype.bind = function(context){
    var self = this;
    var args = Array.prototype.slice.call(arguments);
    return function(){
            self.apply(context,args.slice(1));
        }
    }
}

这就是新的语法赋予 JavaScript 新的能力,如果我们不持续学习新的语法,写出来的代码很难是最简、最优雅、性能最好。当然,阅读其他同学或者开源代码的时候也不一定能看懂。那么 ES6 到底新增或者增强了哪些能力呢?我们来看下图谱:

六、String.prototype.trim()
String.prototype.trim = function(){
    return this.replace(/^s+|s+$/g,"");
}

从这个图谱不能看出 ES6 增加了很多新的语法,比如 Class、Generator、Proxy、Iterator 等。它们可以解决类、异步、代理、自定义遍历等功能。不如我们再来看个小示例:

ECMAScript6部分新增特性简介

一、 let命令和const命令
1、let命令
(1)、let命令,用来声明变量。用法和var类似,区别是let声明的变量只在let所在的代码块中起作用。

if(true){
let a = 0;
var b =1; 
}
console.log(a) // ReferenceError: a is not defined
console.log(b) //1

(for循环的计数器很适合用let命令。)

(2)、let命令不会产生变量提升

console.log(a); //undefined
console.log(b);// ReferenceError: b is not defined
var a= 0;
let b =1;

(3)、暂时性死区,代码块内若使用了let命令,则在使用let命令声明变量之前,该变量都是不可用的,在语法上称为“暂时性死区”(temporal dead zone)

var a = 0;
if(true){
    console.log(a);// ReferenceError: a is not defined
    let a = 0;
}

在let命令声明变量之前,都是变量的“死区”

if(true){
    a = 1; // ReferenceError: a is not defined
    console.log(a); // ReferenceError: a is not defined
    let a;
    console.log(a);//undefined
    a= 123;
    console.log(a);//123
}

(4) let不允许在相同作用域内,重复声明同一个变量

if(true){
    var a= 0;
    let a = 1;
    console.log(a);
}// SyntaxError: Identifier 'a' has already been declared

function testFun(arg){
    let arg = 0;
}
testFun(1); // SyntaxError: Identifier 'arg' has already been declared

function testFun2(arg){
    let arg = 0;
}
testFun2(1);//不报错

(5) 块级作用域的出现,实际上使得获得广泛应用的立即执行匿名函数(IIFE)不再必要了。

//匿名函数写法 
(function(){
    var temp = 0;
})();
//块级作用域写法
{
    let temp = 0;
}

2、const命令
(1)、const声明一个只读的常量。一旦声明,常量的值就不能改变,而且声明的时候必须立即初始化。

const a = 1;
console.log(a);
a= 2;//TypeError: Assignment to constant variable.

const b;//SyntaxError: Missing initializer in const declaration

(2)、const命令的一些特性:块级作用域,不会变量提升,同一作用域内不能重复定义。

(3)、const声明引用型变量时,变量的地址是不能改变的。

const obj = {};
obj.name="nie";
obj = {};// TypeError: Assignment to constant variable.

(4) ES5之中,全局变量会作为全局对象的属性,ES6中全局变量将逐步和全局对象的属性分离,let命令,const命令,class命令声明的全局变量,不属于全局对象的属性。

var a = 123;
console.log(window.a);//123

let b =1;
console.log(window.b);//undefined

实现类与继承。

二、变量的解构赋值

1、 数组的解构赋值:只要等号两边的模式相同,左边的变量就会被赋予对应的值

let [a,b,c] = [1,2,3];
console.log(a);//1
console.log(b);//2
console.log(c);//3

let[d,[e,[f]]] = [1,[2,[3]]];
console.log(d);//1
console.log(e);//2
console.log(f);//3

let[g,h,...i] = [1,2,3,4];

“...i”表示数组,只能放到最后。

let[j,...k,l] = [1,2,3,4,5];// SyntaxError: Rest element must be last element in array

只要某种数据结构具有Iterator接口,都可以采用数组形式的解构赋值
2、对象的解构赋值

var person = {
"name":"nie",
"gender":"male"
}

var {name} = person;
console.log(name);
var {name,gender} = person;
console.log(name);
console.log(gender);

var{name:na,gender:gen} = person;

在 ES6 之前实现类与继承都是借助函数来实现的,在继承方面也是利用原型链。代码如下:

三、数组的扩展

1、 Array.from()
Array.from方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括ES6新增的数据结构Set和Map)。

var objArrLike = {
     '0':1,
     '1':2,
     '2':3,
     length:3
} 

ES5:

var arr = Array.prototype.slice.call(objArrLike);

ES6 :

var arr = Array.from(objArrLike); //[1,2,3]

Array.of : 方法用于将一组值,转换为数组
在ES6中Array还有其他的很多扩展可以自行查找资料

function Component () { this.id = Math.random().toString(36).slice(-5) Object.defineProperty(this, 'id', { writable: false })}const com = new Component()com.id = 3console.log(com.id) // jklls
四、函数的扩展

1、可以为函数的参数指定默认值。
ES5:

function testFun(x,y){
    y = y||"iflytek";
 console.log(x,y);
}
testFun("Hello") //Hello iflytek

ES6:

function testFun(x,y="iflytek"){
    console.log(x,y);
}
testFun("Hello"); //Hello iflytek

2、与解构赋值默认值结合使用

function testFun({x,y = 5}){
    console.log(x,y);
}
testFun({x:1,y:2}); //1 2

也可以用数组的解构赋值对函数参数进行默认值赋值

3、指定了默认值以后函数的长度length将失真(arguments.callee.length) 有相同的效果

(function testFun(a,b,c =2){}).length // 2
function testFun(a,b,c){
    console.log(arguments.callee.length);
} //3

4、rest参数,形式为“...变量名”,用于获取函数的多余参数,rest参数搭配的变量是一个数组。

function sortNums(){
    return Array.prototype.slice.call(arguments).sort();
}
sortNums(3,2,1);//[1,2,3]

var sortNums = (...restArgs)=>{
    return restArgs.sort();
}
sortNums(3,2,1) //[1,2,3]

注意,rest参数之后不能再有其他参数(即只能是最后一个参数),否则会报错。

5、扩展运算符(...) 相当于rest参数的逆运算,将数组转化成逗号分隔的参数序列 注意和[].join(‘,’)的区别

console.log(1,2,...[3,4,5],6) //1 2 3 4 5 6 

扩展运算符可以展开数组,所以不需要apply方法将数组转化成函数的参数了
ES5:

var args = [1,2,3]
function testFun(x,y,z){
       console.log(x,y,z);
}
testFun.apply(null,args); //1 2 3

ES6:

testFun(...args);//1 2 3

6、扩展运算符的应用:
(1)、合并数组
ES5:

[1,2].concat([3,4]);//[1,2,3,4]

ES6:

[1,2,...[3,4]] //[1,2,3,4]

(2)、将字符串转化成字符串数组

[...'123'] //['1','2','3']

注:任何Iterator接口的对象,都可以用扩展运算符转为真正的数组

7、箭头函数
ES6允许使用“箭头”(=>)定义函数

var f = (x)=>x;
var f = function(x){
   return x;
} 

var add = (x,y)=>{
 return x+y;
}

var add = function(x,y){
    return x+y;
}

注意:
(1)、函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
(2)、不可以当做构造函数,不可以使用new命令,报错。
(3)、不可以用arguments对象,该对象在函数体内不存在,用rest参数代替。
(4)、不可以使用yield命令,因此箭头函数不能用作Generator函数。

var id = 1;
function test(){
    setTimeout(function(){
        console.log(this.id);
    });
}
test.call({id:2}) //1

function test(){
    setTimeout(()=>{
        console.log(this.id);
    });
}
test.call({id:2}) //2

这段代码的含义是定义一个组件类,类定义了一个属性 id,这个 id 是随机、只读的。ES6 有了专门的语法来定义类。

五、对象的扩展

1、 ES6允许直接写入变量和函数,作为对象的属性和方法。ES6允许对象之中只写属性名,不写属性值。此时,属性值等于属性名所代表的变量。

var name = "iflytek";
    var person = {
    name,
    gender:'male',
    sayName(){
        console.log(name),
    }
}

2、 属性名表达式,ES6允许字面量定义对象时,用方法二(表达式)作为对象的属性名,即把表达式放在方括号内
ES5:

var person={
    name:"nie",
    gender:"male"
}

ES6:

var person = {
    ["name"]:"nie",
    ["gen"+"der"]:"male"
}

增强的对象字面量
对象字面量被增强了,写法更加简洁与灵活,同时在定义对象的时候能够做的事情更多了:
(1)可以在对象字面量里面定义原型
(2)定义方法可以不用function关键字
(3)直接调用父对象方法

var person = {
    name:"nie",
    sayName(){
        console.log(this.name);
    }
}

var malePerson = {
    __proto__:person,
    gender:"male",
    sayGender(){
        console.log(this.gender);
    }
}

malePerson.name;//nie
malePerson.sayName();//nie
class Component { constructor () { this.id = Math.random().toString(36).slice(-5) Object.defineProperty(this, 'id', { writable: false }) }}const com = new Component()com.id = 3console.log(com.id)
六、Symbol类型

ES6引入了一种新的原始数据类型Symbol,表示独一无二的值。它是JavaScript语言的第七种数据类型,前六种是:Undefined、Null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)。

对象的属性名现在可以有两种类型,一种是原来就有的字符串,另一种就是新增的Symbol类型。凡是属性名属于Symbol类型,就都是独一无二的,可以保证不会与其他属性名产生冲突

var symbolStr = Symbol(); //作为对象的属性,用法和字符串一样的

第一种写法:

var a={}
a[symbolStr] = "iflytek";

10bet,第二种写法:

var a = {
    [symbolStr]:"iflytek"
}

第三种写法:

var a= {};
Object.defineProperty(a,symbol,{value:"iflytek"})

a[symbolStr]//iflytek

在语义上看 ES6 的写法更容易读懂。不信,我们在看下继承的写法:

七、Proxy概述

Proxy用于修改某些操作的默认行为,等同于在语言层面做出修改。Proxy可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。

实例:

var target = {name:"nie"};

var obj = new Proxy(target,{
    set:function(target,key,reciver){
    console.log("set "+ key +"value :" +reciver);
        //target[key] = reciver;
        return Reflect.set(target,key,reciver);
    }
});

下面是Proxy支持的拦截操作一览。
对于可以设置、但没有设置拦截的操作,则直接落在目标对象上,按照原先的方式产生结果。
(1) get(target, propKey, receiver)
拦截对象属性的读取,比如proxy.foo和proxy['foo']。
最后一个参数receiver是一个对象,可选,参见下面Reflect.get的部分。
(2) set(target, propKey, value, receiver)
拦截对象属性的设置,比如proxy.foo = v或proxy['foo'] = v,返回一个布尔值。
(3) has(target, propKey)
拦截propKey in proxy的操作,以及对象的hasOwnProperty方法,返回一个布尔值。
(4) deleteProperty(target, propKey)
拦截delete proxy[propKey]的操作,返回一个布尔值。
(5) ownKeys(target)
拦截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy),返回一个数组。该方法返回对象所有自身的属性,而Object.keys()仅返回对象可遍历的属性。
(6) getOwnPropertyDescriptor(target, propKey)
拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。
(7) defineProperty(target, propKey, propDesc)
拦截Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs),返回一个布尔值。
(8) preventExtensions(target)
拦截Object.preventExtensions(proxy),返回一个布尔值。
(9) getPrototypeOf(target)
拦截Object.getPrototypeOf(proxy),返回一个对象。
(10) isExtensible(target)
拦截Object.isExtensible(proxy),返回一个布尔值。
(11) setPrototypeOf(target, proto)
拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。
如果目标对象是函数,那么还有两种额外操作可以拦截。
(12) apply(target, object, args)
拦截Proxy实例作为函数调用的操作,比如proxy(...args)、proxy.call(object, ...args)、proxy.apply(...)。
(13) construct(target, args)
拦截Proxy实例作为构造函数调用的操作,比如new proxy(...args)。

function Component () { this.id = Math.random().toString(36).slice(-5) Object.defineProperty(this, 'id', { writable: false })}function SubComponent () { Component.call(this)}SubComponent.prototype = Component.prototypeconst com = new SubComponent()com.id = 3console.log(com.id)

class Component { constructor () { this.id = Math.random().toString(36).slice(-5) Object.defineProperty(this, 'id', { writable: false }) }}class SubComponent extends Component {}const com = new SubComponent()com.id = 3console.log(com.id)
八、Reflect概述

(1) 将Object对象的一些明显属于语言内部的方法(比如Object.defineProperty),放到Reflect对象上。现阶段,某些方法同时在Object和Reflect对象上部署,未来的新方法将只部署在Reflect对象上。
(2) 修改某些Object方法的返回结果,让其变得更合理。比如,Object.defineProperty(obj, name, desc)在无法定义属性时,会抛出一个错误,而Reflect.defineProperty(obj, name, desc)则会返回false。
(3) 让Object操作都变成函数行为。某些Object操作是命令式,比如name in obj和delete obj[name],而Reflect.has(obj, name)和Reflect.deleteProperty(obj, name)让它们变成了函数行为。

(4) Reflect对象的方法与Proxy对象的方法一一对应,只要是Proxy对象的方法,就能在Reflect对象上找到对应的方法。这就让Proxy对象可以方便地调用对应的Reflect方法,完成默认行为,作为修改行为的基础。也就是说,不管Proxy怎么修改默认行为,你总可以在Reflect上获取默认行为。

Reflect对象的方法清单如下,共13个。
• Reflect.apply(target,thisArg,args)
• Reflect.construct(target,args)
• Reflect.get(target,name,receiver)
• Reflect.set(target,name,value,receiver)
• Reflect.defineProperty(target,name,desc)
• Reflect.deleteProperty(target,name)
• Reflect.has(target,name)
• Reflect.ownKeys(target)
• Reflect.isExtensible(target)
• Reflect.preventExtensions(target)
• Reflect.getOwnPropertyDescriptor(target, name)
• Reflect.getPrototypeOf(target)
• Reflect.setPrototypeOf(target, prototype)
上面这些方法的作用,大部分与Object对象的同名方法的作用都是相同的,而且它与Proxy对象的方法是一一对应的。

上下代码对比可以看出来 ES6 的方式要舒服很多,也更容易阅读。借助这个题我们再来思考 ES6 这个写法还能继续优化吗?比如 Object.defineProperty 方法在构造函数里显得那么格格不入。有没有更优雅的写法呢?不妨试试 ES6 新的语法 Proxy?

九、Set/Map 概述

1、ES6提供了新的数据结构Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。

3、 JavaScript的对象(Object),本质上是键值对的集合(Hash结构),但是传统上只能用字符串当作键。这给它的使用带来了很大的限制。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object结构提供了“字符串—值”的对应,Map结构提供了“值—值”的对应,是一种更完善的Hash结构实现。如果你需要“键值对”的数据结构,Map比Object更合适。

本文由10bet发布于Web前端,转载请注明出处:ECMAScript5、【10bet】ECMAScript6 新增知识点总结

关键词:

上一篇:没有了

下一篇:没有了

最火资讯