ES5 浏览器可用性
https://www.caniuse.com/#search=es5
ES6 浏览器可用性
https://www.caniuse.com/#search=es6
JS
包含三个部分:ECMAScript(核心)
,DOM(文档对象模型)
,BOM(浏览器对象模型)
。ECMAScript
是JS
语言的基础。
ECMAScript
的最新版是第六版ECMAScript 6
,于2015年6月17日发布,截止发布日期,JavaScript
的官方名称是ECMAScript 2015
,是当前最新的正式规范。
ECMAScript
的各个版本:(从第三版开始说)
ECMAScript3
新增了对正则表达式、新控制语句、try-catch异常处理的支持,修改了字符处理、错误定义和数值输出等内容。标志着ECMAScript成为了一门真正的编程语言。ECMAScript5
力求澄清第3版中的歧义,并添加了新的功能。
新功能包括:原生JSON对象、继承的方法、高级属性的定义以及引入严格模式。ECMAScript6
是继ES5之后的一次主要改进。
增添了许多必要的特性,例如:模块和类以及一些实用特性,Maps、Sets、Promises、生成器(Generators)等。支持IE9及以上
最常用的就是JSON对象了,早期的浏览器就要加载js插件来实现。
为整个脚本文件开启严格模式,需要在所有语句之前放一个特定语句 "use strict";
(或 'use strict';
)
// 整个脚本都开启严格模式的语法
"use strict";
var v = "Hi! I'm a strict mode script!";
严格模式,严格模式,重要其实就是es5中弃用了很多以前版本的语法,你再用就提示错误。
一句话说明了这个的用途,我们只要知道哪些语法被弃用了(es3,es2,es1),就知道哪些会在使用这个模式下报错了。
简单罗列弃用限制和es5不符合语法定义限制:
eval
的操作都会被禁止// 声明一个“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.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]]);
}
都能起到改变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)
indexOf
返回根据给定元素找到的第一个索引值,否则返回-1
lastIndexOf
方法返回指定元素在数组中的最后一个的索引,如果不存在则返回 -1
判断是否为数组
"use strict"
var str = "aa";
var arr = [1,2,3,4,5,6];
console.info(Array.isArray(str));
console.log(Array.isArray(arr));
使用foreach遍历数组的话,使用break不能中断循环,使用return也不能返回到外层函数。
遍历循环数组
//这样就方便计算数组中数据了
arr.forEach(function(item,index,array){
console.log("索引项:"+index+" 数组项:"+item);
})
处理原数组,返回处理后的值。
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和上面的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);
判断数组中是否有满足需要的值,返回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);
判断数组中是否所有值都满足需要,返回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);
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 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之后
${}
来界定;$("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
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 操作符:
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]
// 对象解构
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);
非常好用
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 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()。
有几点值得注意的是:
实际应用中大都用ES7的async/await来替代
由于 Generator 函数返回的遍历器对象,只有调用next方法才会遍历下一个内部状态,所以其实提供了一种可以暂停执行的函数。yield表达式就是暂停标志。
需要注意
let s = (yield 1+2)
,s其值将会是undefined,
而1+2这个等于3的值将会作为next返回对象的value的值由于next方法的参数表示上一个yield表达式的返回值,所以在第一次使用next方法时,传递参数是无效的。 V8 引擎直接忽略第一次使用next方法时的参数,只有从第二次使用next方法开始,参数才是有效的。从语义上讲,第一个next方法用来启动遍历器对象,所以不用带有参数。
如果在 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"
不传值
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,所以我们一定要把之前的结果传进去。
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 的值)。
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
代码块捕获。
这意味着,出错的代码与处理错误的代码,实现了时间和空间上的分离,这对于异步编程无疑是很重要的。
下面看看如何使用 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 命令。
非常好用
原来的写法
update_click:function(){
this.$nextTick(function(){
// ...
});
}
新写法
async update_click:function(){
// ...
await this.$nextTick();
// ...
}
Math.pow(3, 2) === 3 ** 2 // 9
数组原型的方法,查找一个数值是否在数组中,只能判断一些简单类型的数据,对于复杂类型的数据无法判断。该方法接受两个参数,分别是查询的数据和初始的查询索引值。
[1, 2, 3].indexOf(3) > -1 // true
// 等同于:
[1, 2, 3].includes(3) // true
两者的优缺点和使用场景
该方法会将某个对象的可枚举属性与值按照二维数组的方式返回。(如果目标对象是数组,则会将数组的下标作为键值返回)
Object.entries({ one: 1, two: 2 }) //[['one', 1], ['two', 2]]
Object.extries([1, 3]) //[['0', 1], ['1', 3]]
它的工作原理和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