专栏首页码客JS ES各版本特性

JS ES各版本特性

版本历史

ES5 浏览器可用性

https://www.caniuse.com/#search=es5

ES6 浏览器可用性

https://www.caniuse.com/#search=es6

JS包含三个部分:ECMAScript(核心)DOM(文档对象模型)BOM(浏览器对象模型)ECMAScriptJS语言的基础。 ECMAScript的最新版是第六版ECMAScript 6,于2015年6月17日发布,截止发布日期,JavaScript的官方名称是ECMAScript 2015,是当前最新的正式规范。

ECMAScript的各个版本:(从第三版开始说)

  • 第三版ECMAScript3 新增了对正则表达式、新控制语句、try-catch异常处理的支持,修改了字符处理、错误定义和数值输出等内容。标志着ECMAScript成为了一门真正的编程语言。
  • 第四版于2008年7月发布前被废弃。
  • 第五版ECMAScript5力求澄清第3版中的歧义,并添加了新的功能。 新功能包括:原生JSON对象、继承的方法、高级属性的定义以及引入严格模式。
  • 第六版ECMAScript6是继ES5之后的一次主要改进。 增添了许多必要的特性,例如:模块和类以及一些实用特性,Maps、Sets、Promises、生成器(Generators)等。

ES5

支持IE9及以上

最常用的就是JSON对象了,早期的浏览器就要加载js插件来实现。

严格模式

严格模式详解

为整个脚本文件开启严格模式,需要在所有语句之前放一个特定语句 "use strict"; (或 'use strict';

// 整个脚本都开启严格模式的语法
"use strict";
var v = "Hi!  I'm a strict mode script!";

严格模式,严格模式,重要其实就是es5中弃用了很多以前版本的语法,你再用就提示错误。

一句话说明了这个的用途,我们只要知道哪些语法被弃用了(es3,es2,es1),就知道哪些会在使用这个模式下报错了。

简单罗列弃用限制和es5不符合语法定义限制:

  1. 不使用var声明变量严格模式中将不通过
  2. 任何使用eval的操作都会被禁止
  3. eval作用域
  4. with被禁用
  5. caller/callee 被禁用
  6. 对禁止扩展的对象添加新属性会报错
  7. 删除系统内置的属性会报错
  8. delete使用var声明的变量或挂在window上的变量报错
  9. delete不可删除属性(isSealed或isFrozen)的对象时报错
  10. 对一个对象的只读属性进行赋值将报错
  11. 对象有重名的属性将报错
  12. 函数有重名的参数将报错
  13. 八进制表示法被禁用
  14. arguments严格定义为参数,不再与形参绑定
  15. 函数必须声明在顶层
  16. ES5里新增的关键字不能当做变量标示符使用,如implements, interface, let, package, private, protected, public, static, yield
  17. call/apply的第一个参数直接传入不包装为对象
  18. call/apply的第一个参数为null/undefined时,this为null/undefined
  19. bind的第一个参数为null/undefined时,this为null/undefined

JSON对象

// 声明一个“JSON格式”的String 
var jsonStr = '{"a":"1","b":2,"c":"13"}';
// JSON.parse把String转成Object了
var jsonObj = JSON.parse(jsonStr);
console.log(jsonStr);
console.log(jsonObj);
console.log(JSON.stringify(jsonObj));

// 转换JSON时转换类型
var jsonObj2 = JSON.parse(jsonStr, function (key, value) {
    if (typeof value == 'string') {
        return parseInt(value);
    } else {
        return value;
    }
});
console.log(jsonObj2);

// JSON.stringify 可以用来对Json对象进行简单过滤
var jsonStr2 = JSON.stringify(jsonObj, function (key, value) {
    if (value == 13) {
        return undefined; //使用undefined,则键值对就不会包含在最终的JSON中
    } else {
        return value;
    }
});
console.log("这里就少了值为13的键值");
console.log(jsonStr2);

// JSON.stringify还可以用来做Json格式化
var jsonStr3 = JSON.stringify(jsonObj, null, 4);
console.log(jsonStr3);

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 返回一个由给定对象的所有可枚举自身属性的属性名组成的数组

举个例子怎么用的,这样就可以简单遍历对象了,又贴代码了

var obj = {
    a: "1",
    b: 2,
    c: "13"
}
console.log(typeof obj);
let mkeys = Object.keys(obj);
for (var i = 0; i < mkeys.length; i++) {
    console.log(obj[mkeys[i]]);
}

bind/apply/call

详情参见

都能起到改变this的效果

"use strict"

function locate(){
  console.log(this.location);
}
function Maru(location){
  this.location = location;
}
var kitty = new Maru("cardboard box");

var locateMaru = locate.bind(kitty);
locateMaru();
//apply和call 立即执行,而bind如果不调用是不执行的
locate.apply(kitty)
locate.call(kitty)

Array对象

indexOf/lastIndexOf

indexOf 返回根据给定元素找到的第一个索引值,否则返回-1

lastIndexOf 方法返回指定元素在数组中的最后一个的索引,如果不存在则返回 -1

isArray

判断是否为数组

"use strict"
var str = "aa";
var arr = [1,2,3,4,5,6];
console.info(Array.isArray(str));
console.log(Array.isArray(arr));

forEach

使用foreach遍历数组的话,使用break不能中断循环,使用return也不能返回到外层函数。

遍历循环数组

//这样就方便计算数组中数据了
arr.forEach(function(item,index,array){
    console.log("索引项:"+index+"  数组项:"+item);
})

map

处理原数组,返回处理后的值。

var arrObj = [
    {name:"one",age:1},
    {name:"two",age:2},
    {name:"three",age:3},
    {name:"four",age:4},
    {name:"five",age:5},
    {name:"six",age:6},
    {name:"seven",age:7},
];
var ages = arrObj.map(function(obj){
    return obj.age;//返回值
});
console.log(ages);

filter

过滤器,根据自己的条件进行过滤,filter和上面的map还是有点小区别的,自己发现吧。

"use strict"

var arrObj = [
    {name:"one",age:1},
    {name:"two",age:2},
    {name:"three",age:3},
    {name:"four",age:4},
    {name:"five",age:5},
    {name:"six",age:6},
    {name:"seven",age:7},
];
var arrFilter = arrObj.filter(function(obj){
    return obj.age>5;//返回值的判断条件
});
console.log(arrFilter);

some

判断数组中是否有满足需要的值,返回true/false

"use strict"

var arrObj = [
    {name:"one",age:1},
    {name:"two",age:2},
    {name:"three",age:3},
    {name:"four",age:4},
    {name:"five",age:5},
    {name:"six",age:6},
    {name:"seven",age:7},
];
var somefour = arrObj.some(function(obj){
    return obj.age>4;//判断age有没有大于4的,如果有返回true,没有返回false
});
var someten = arrObj.some(function(obj){
    return obj.age>10;//判断age有没有大于10的,如果有返回true,没有返回false
});
console.log("判断有没有大于4的:"+somefour);
console.log("判断有没有大于10的:"+someten);

every

判断数组中是否所有值都满足需要,返回true/false

"use strict"

var arrObj = [
    {name:"one",age:1},
    {name:"two",age:2},
    {name:"three",age:3},
    {name:"four",age:4},
    {name:"five",age:5},
    {name:"six",age:6},
    {name:"seven",age:7},
];

var everyfour = arrObj.every(function(obj){
    return obj>4;
});
var everyten = arrObj.some(function(obj){
    return obj.age>10;//判断age有没有大于10的,如果有返回true,没有返回false
});
console.log("判断是不是所有都大于4:"+everyfour);
console.log("判断是不是所有都大于10:"+everyten);

ES6

IE压根不支持,Edge支持

箭头操作符

方便写回调,不改变this的引用。

var array = [1, 2, 3];
//传统写法
array.forEach(function(item, index, arr) {
    console.log(item);
});
//ES6
array.forEach(item = > console.log(item));

变量定义let和const

  • let声明变量(块级作用域),let是更完美的var,它声明的全局变量不是全局属性widow的变量,这便解决了for循环中变量覆盖的问题
  • const声明常量(块级作用域)

字符串新增方法

let str = 'happy';
console.log(str.includes('ha')) // true
console.log(str.repeat(3)) // 'happyhappyhappy'
console.log(str.startsWith('ha')); // true,参数2为查找的位置
console.log(str.endsWith('p', 4)); // true,参数2为查找的字符串长度

字符串模版

ES6之前

通过\+来构建模板

$("body").html("This demonstrates the output of HTML \
content to the page, including student's\
" + name + ", " + seatNumber + ", " + sex + " and so on.");

ES6之后

  1. 基本的字符串格式化。将表达式嵌入字符串中进行拼接。用${}来界定;
  2. ES6反引号直接搞定;
$("body").html(`This demonstrates the output of HTML content to the page, 
including student's ${name}, ${seatNumber}, ${sex} and so on.`);

函数参数默认值

// ES6之前,当未传入参数时要实现:text = 'default';
function printText(text) {
    text = text || 'default';
    console.log(text);
}

// ES6;
function printText(text = 'default') {
    console.log(text);
}

printText('hello'); // hello
printText();// default

对象合并Object.assign()

const obj1 = {
    a: 1
}

const obj2 = {
    c: 2
}
const obj = Object.assign(obj1, obj2)
console.log(obj); // {a: 1, c: 2}
console.log(obj === obj1); // true

键值对重名简写

function people(name, age) {
    return {
        name,
        age
    };
}

对象字面量简写

const people = {
    name: 'lux',
    getName () {    // 省略冒号(:)和function关键字
        console.log(this.name)
    }
}

Spread / Rest 操作符

Spread / Rest 操作符指的是...,具体是 Spread 还是 Rest 需要看上下文语境。

当被用于迭代器中时,它是一个 Spread 操作符:

function foo(x,y,z) {
  console.log(x,y,z);
}
 
let arr = [1,2,3];
foo(...arr); // 1 2 3

当被用于函数传参时,是一个 Rest 操作符:当被用于函数传参时,是一个 Rest 操作符:

function foo(...args) {
  console.log(args);
}
foo( 1, 2, 3, 4, 5); // [1, 2, 3, 4, 5]

对象和数组解构和Rest操作符

// 对象解构
const people = {
    name: 'xiaoming',
    age: 25
}
const {
    name,
    age
} = people; // 'xiaoming', 25

// rest参数,返回的是一个对象
const obj = {
    a: 2,
    b: 3,
    c: 4,
    d: 5
};
const {
    a,
    ...rest
} = obj; // 2 { b: 3, c: 4, d: 5 }

console.log(rest);

// 数组解构 
const arr = [1, 3, 4];
const [x, y, z] = arr; // 1, 3, 4
const [x2, ...rest2] = arr; // 1, [3,4]
console.log(rest2);

Promise

非常好用

详情参见

Set

Set作为ES6新的数据解构(类数组),它的成员都是唯一的,因为最直接的使用场景便是去重、并、差、交集的使用。它使用的算法叫做“Same-value-zero equality”,类似精确运算的===,主要是NaN,这里它将两个视为相等。

// Set实例的常用方法和属性add,delete,clear,has、size
const s = new Set(['A', 'B', 'C']);
console.log(s); // Set { 'A', 'B', 'C' }
console.log(s.has('A')) // true,bool值
console.log(s.size) // 3
console.log(s.clear()) // Set {}
console.log(s.delete('A')) // true,bool值

同时Set实例配合常用遍历方法,实现并、交、差集。

const a =[1, 2, 3]
const b = [2, 3, 4];
// 并集
const s = Array.from(new Set([...a, ...b])); // [ 1, 2, 3, 4 ]
// 交集、差集
const bSet = new Set(b);
const interestSet = a.filter(v => bSet.has(v)); // [ 2, 3 ]
const interestSet = a.filter(v => !bSet.has(v)); // [ 1 ]

二进制和八进制字面量

ES6 支持二进制和八进制的字面量,通过在数字前面添加 0o 或者0O 即可将其转换为八进制值:

let oValue = 0o10;
console.log(oValue); // 8
 
let bValue = 0b10; // 二进制使用 `0b` 或者 `0B`
console.log(bValue); // 2

for…of 和 for…in

for in遍历的是数组的索引(即键名),而for of遍历的是数组元素值。

for…of:

let letters = ['a', 'b', 'c'];
for (let letter of letters) {
    console.log(letter);
}
// 结果:a,b,c

for…in:

let letters = ['a', 'b', 'c'];
for (let letter in letters) {
    console.log(letter);
}
// 结果:0,1,2

对象超类

ES6 允许在对象中使用 super 方法:

var parent = {
  foo() {
    console.log("Hello from the Parent");
  }
}
 
var child = {
  foo() {
    super.foo();
    console.log("Hello from the Child");
  }
}
 
Object.setPrototypeOf(child, parent);
child.foo(); // Hello from the Parent
             // Hello from the Child

ES6 中支持 class 语法,不过,ES6的class不是新的对象继承模型,它只是原型链的语法糖表现形式。

函数中使用 static 关键词定义构造函数的的方法和属性:

class Student {
  constructor() {
    console.log("I'm a student.");
  }

  study() {
    console.log('study!');
  }
 
  static read() {
    console.log("Reading Now.");
  }
}

console.log(typeof Student); // function
let stu = new Student(); // "I'm a student."
stu.study(); // "study!"
stu.read(); // "Reading Now."

类中的继承和超集:

class Phone {
  constructor() {
    console.log("I'm a phone.");
  }
}

class MI extends Phone {
  constructor() {
    super();
    console.log("I'm a phone designed by xiaomi");
  }
}

let mi8 = new MI();

extends 允许一个子类继承父类,需要注意的是,子类的constructor 函数中需要执行 super() 函数。 当然,你也可以在子类方法中调用父类的方法,如super.parentMethodName()。

有几点值得注意的是:

  • 类的声明不会提升(hoisting),如果你要使用某个 Class,那你必须在使用之前定义它,否则会抛出一个 ReferenceError 的错误
  • 在类中定义函数不需要使用 function 关键词

Generator

实际应用中大都用ES7的async/await来替代

yield

由于 Generator 函数返回的遍历器对象,只有调用next方法才会遍历下一个内部状态,所以其实提供了一种可以暂停执行的函数。yield表达式就是暂停标志。

  1. 遇到yield表达式,就暂停执行后面的操作,并将紧跟在yield后面的那个表达式的值,作为返回的对象的value属性值。
  2. 下一次调用next方法时,再继续往下执行,直到遇到下一个yield表达式。
  3. 如果没有再遇到新的yield表达式,就一直运行到函数结束,直到return语句为止,并将return语句后面的表达式的值,作为返回的对象的value属性值。
  4. 如果该函数没有return语句或者执行完return之后再运行next的时候,则返回的对象的value属性值为undefined,done为true。

需要注意

  • yield表达式只能用在 Generator 函数里面,用在其他地方都会报错。
  • yield表达式如果用在另一个表达式之中,必须放在圆括号里面
  • yield表达式用作函数参数或放在赋值表达式的右边,可以不加括号。
  • yield放在表达式中的时候,let s = (yield 1+2),s其值将会是undefined, 而1+2这个等于3的值将会作为next返回对象的value的值

next

  • yield表达式本身没有返回值,或者说总是返回undefined。
  • next方法可以带一个参数,该参数就会被当作上一个yield表达式的返回值。

由于next方法的参数表示上一个yield表达式的返回值,所以在第一次使用next方法时,传递参数是无效的。 V8 引擎直接忽略第一次使用next方法时的参数,只有从第二次使用next方法开始,参数才是有效的。从语义上讲,第一个next方法用来启动遍历器对象,所以不用带有参数。

yield*

如果在 Generator 函数内部,调用另一个 Generator 函数,默认情况下是没有效果的。 这个就需要用到yield*表达式,用来在一个 Generator 函数里面执行另一个 Generator 函数。

function* inner() {
  yield 'hello!';
}

function* outer1() {
  yield 'open';
  yield inner();
  yield 'close';
}

var gen = outer1()
gen.next().value // "open"
gen.next().value // 返回一个遍历器对象
gen.next().value // "close"

function* outer2() {
  yield 'open'
  yield* inner()
  yield 'close'
}

var gen = outer2()
gen.next().value // "open"
gen.next().value // "hello!"
gen.next().value // "close"

示例1

不传值

function* test() {
    var str1 = yield "hello";
    var str2 = yield str1 + " world";
    return str2;
}

var t = test();
console.log(t.next()); // {value: "hello", done: false}
console.log(t.next()); // {value: "undefined world", done: false}
console.log(t.next()); // {value: undefined, done: true}

传值

function* test() {
    var str1 = yield "hello";
    var str2 = yield str1 + " world";
    return str2;
}

var t = test();
var t1 = t.next();
var t2 = t.next(t1.value);
var t3 = t.next(t2.value);
console.log(t1); // {value: "hello", done: false}
console.log(t2); // {value: "hello world", done: false}
console.log(t3); // {value: "hello world", done: true}

上面的两个例子可以发现yield修饰的表达式的返回值为undefined,所以我们一定要把之前的结果传进去。

示例2

function* test01(x) {
    var y = yield x + 1;
    return y;
}

var t1 = test01(1);
console.log(t1.next()) // {value: 2, done: false}
console.log(t1.next(3)) // {value: 3, done: true}

第一个 next 方法的 value 属性,返回表达式 x + 1 的值(2)。 第二个 next 方法带有参数3,这个参数可以传入 Generator 函数,作为上个阶段异步任务的返回结果,被函数体内的变量 y 接收。因此,这一步的 value 属性,返回的就是3(变量 y 的值)。

示例3

Generator 函数内部还可以部署错误处理代码,捕获函数体外抛出的错误。

function* test02(x) {
    try {
        var y = yield x + 2;
    } catch (e) {
        console.log(e);
    }
    return y;
}

var t2 = test02(1);
console.log(t2.next());
console.log(t2.throw("出错了"));

上面代码的最后一行,Generator 函数体外,使用指针对象的 throw 方法抛出的错误,可以被函数体内的 try ... catch 代码块捕获。

这意味着,出错的代码与处理错误的代码,实现了时间和空间上的分离,这对于异步编程无疑是很重要的。

示例4

下面看看如何使用 Generator 函数,执行一个真实的异步任务。

function loaddata() {
    var request = new XMLHttpRequest();
    request.open('GET', 'login.json', false);
    request.send(null);
    if (request.status === 200) {
        return request.responseText;
    }
}

function* mytest() {
    let data = yield loaddata();
    console.log(data);
}

var mtest = mytest();
let mn1 = mtest.next()
console.info(mn1);
let mn2 = mtest.next(mn1.value)
console.info(mn2);

输出的结果

{value: “{“code”: 0, “msg”: “success”}”, done: false} { “code”: 0, “msg”: “success”} {value: undefined, done: true}

这三行的输出分别对应上面的最后三行代码

上面代码中,Generator 函数封装了一个异步操作,该操作先读取一个远程接口,然后从 JSON 格式的数据解析信息。

就像前面说过的,这段代码非常像同步操作,除了加上了 yield 命令。

ES7

async/await

非常好用

详情参见

原来的写法

update_click:function(){
    this.$nextTick(function(){
    	// ...
	});
}

新写法

async update_click:function(){
    // ...
    await this.$nextTick();
    // ...
}

求幂运算符

Math.pow(3, 2) === 3 ** 2    // 9

Array.prototype.includes()

数组原型的方法,查找一个数值是否在数组中,只能判断一些简单类型的数据,对于复杂类型的数据无法判断。该方法接受两个参数,分别是查询的数据和初始的查询索引值。

[1, 2, 3].indexOf(3) > -1 // true
// 等同于:
[1, 2, 3].includes(3) // true

两者的优缺点和使用场景

  • 简便性 includes方法略胜一筹,直接返回bool。indexOf需要返回数组下标,我们需要对下标值在进行操作,进而判断是否在数组中。
  • 精确性 两者这都是通过===进行数据处理,但是对NaN数值的处理行为不同。includes对NaN的处理不会遵循严格模式去处理,所以返回true。indexOf会按照严格模式去处理,返回-1。 [1, 2, NaN].includes(NaN) // true [1, 2, NaN].indexOf(NaN) // -1
  • 使用场景 如果仅仅查找数据是否在数组中,建议使用includes,如果是查找数据的索引位置,建议使用indexOf更好一些

ES8

Object.entries()

该方法会将某个对象的可枚举属性与值按照二维数组的方式返回。(如果目标对象是数组,则会将数组的下标作为键值返回)

Object.entries({ one: 1, two: 2 })    //[['one', 1], ['two', 2]]
Object.extries([1, 3])    //[['0', 1], ['1', 3]]

Object.values()

它的工作原理和Object.entries()方法很像,但是它只返回键值对中的值,结果是一维数组

Object.values({one: 1, two: 2})    // [1, 2]
Object.values({3: 'a', 1: 'b', 2: 'c'}) // ['b', 'c', 'a'] 
Object.extries([1, 3])     //[1, 3]

字符串填充

ES8提供了新的字符串填充方法,该方法可以使得字符串达到固定长度。它有两个参数,字符串目标长度和填充内容。

console.info('vue'.padStart(6, 'm')); // mmmvue
console.info('vue'.padEnd(6, 'm')); // vuemmm
console.info('vue'.padStart(3, 'm')); //vue

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Electron中利用fluent-ffmpeg进行视频推流

    剑行者
  • 页面DOM导出PDF

    剑行者
  • iOS Array和Dictionary常用方法

    剑行者
  • JavaScript变量和数据类型

    1.JavaScript变量 javascript的变量是松散类型的(弱类型),也就是说一个变量可以用来保存任何类型的数据,每个变量仅仅是用来保存值的占位符。变...

    水击三千
  • javascript正则表达式RegExp再次研究-replace

    虽然有那么多属性,但是基本用不到,为什么呢?因为这些信息都包含在模式声明中了: 字面量创建的正则

    陌上寒
  • 前端day11-JS学习笔记(构造函数、对象的API、作用域、arguments关键字)

    当我们想要创建多个对象时很容易造成代码冗余,那么我们可以使用函数或者自定义构造函数去创建对象。

    帅的一麻皮
  • javascript(三):对象

     对象(object)是javascript中很重要的数据类型。对象是“键值对”的集合,同时也是无序的。(注意:对象结尾处有分号) 1 var ob1={ ...

    用户1149564
  • ES6常用知识点小结

    ECMAScript 6(以下简称ES6)是JavaScript语言的下一代标准。

    柳醉梦声
  • js程序设计02——变量、作用域问题

    首先,ECMAScript中的数据类型分为基本类型、引用类型,基本类型的访问操作是按值的。引用类型的值是保存在内存中的对象,操作对象时,实际上操作的是对象的引用...

    用户1141560
  • 37个JavaScript基本面试问题和解答(建议收藏)

    1、使用typeof bar ===“object”来确定bar是否是一个对象时有什么潜在的缺陷?这个陷阱如何避免?

    用户1272076

扫码关注云+社区

领取腾讯云代金券