ES6常用语法
ES6 常用语法
- 新增 let、const 命令 来声明变量,和var 相比,let 声明的变量不存在变量提升问题,但没有改变JS弱类型的特点,依然可以接受任意类型变量的声明;const 声明的变量不允许在后续逻辑中改变,提高了JS语法的严谨性。
- 新增解构赋值、rest 语法、箭头函数等,这些都是为了让代码看起来更简洁,而包装的语法糖。
- 新增模块化机制,这是 JavaScript 走向规范比较重要的一步,让前端更方便的实现工程化。
- 新增类和继承的概念,配合模块化,JavaScript 也可以实现高复用、高扩展的系统架构。
- 新增模板字符串功能,高效简洁,结束拼接字符串的时代。
- 新增 Promise 机制,解决异步回调多层嵌套的问题。
- 更多学习:点击
一、声明变量const let var
- var,变量提升,是全局作用域或函数作用域,var声明的变量存在覆盖现象
- let,表示变量,是块级作用域,比如一个函数内部,代码块{}内部,let不允许在相同作用域内,重复声明同一个变量。属于局部作用域,没有覆盖现象。不会污染全局变量
- const,表示常量,是块级作用域,比如一个函数内部,代码块{}内部,常量一旦声明不可修改,属于局部作用域。
- ES5 只有全局作用域和函数作用域,没有块级作用域
<script> { let x = 10; var y = 20; } console.log (x); // ReferenceError: x is not defined console.log (y); // 20 console.log(a); // undefined,var声明变量之前可以使用该变量 var a = 10; console.log(b); // ReferenceError: b is not defined,let声明变量之前不可以使用该变量 let b = 10; //注意:let不允许在相同的作用域内重复声明同一个变量。 function foo(){ let x = 10; var x = 20; } // 报错 function foo(){ let y = 10; let y = 20; } // 报错 //ES5中只有全局作用域和函数作用域,并没有块级作用域。 var name = 'Tom'; function foo(){ console.log(name); if (false){ var name = 'Bob' } } foo(); // undefined /*出现上述现象的原因就是在函数内部,由于变量提升导致内存的name变量覆盖了外层的name变量。 类似的情况还出现在 for循环的计数变量最后会泄露为全局变量。*/ for (var i=0;i<5;i++){ console.log('哈哈'); } console.log(i); // 5 // ES6中的let声明变量的方式实际上就为JavaScript新增了块级作用域。 var name = 'Tom'; function foo(){ console.log(name); if (false){ let name = 'Bob' } } foo(); // Tom // 此时,在foo函数内容,外层代码块就不再受内层代码块的影响。所以类似for循环的计数变量我们最好都是用let来声明。 var a = []; for (var i = 0; i < 10; i++) { a[i] = function () { console.log(i) } } a[6](); //10 //=============== var a = []; for (let i = 0; i < 10; i++) { a[i] = function () { console.log(i) } } a[6](); //6 //=============== var a = []; for (let i = 0; i < 4; i++) { console.log(i) // 0,1,2,3 } /* const用来声明常量。const声明变量必须立即初始化,并且其值不能再改变。 const声明常量的作用域与let相同,只在声明所在的块级作用域内有效。 ES6规定:var命令和function命令声明的全局变量依旧是全局对象的属性; let命令、const命令和class命令声明的全局变量不属于全局对象的属性。 */ const PI = 3.14; var x1 = 10; let y1 = 20; window.x1 // 10 window.y1 // undefined </script>
<script type="text/javascript"> //块级作用域 // { // var a = 10; // let b = 20; // } // console.log(a); // 10 // console.log(b); // b is not defined //let声明不存在变量提升 // var a;//js解释器会把下方的变量'a'被提到了代码最上方提前声明 // console.log(a) // undefined // var a = 10; // //--------------------------------- // let b;//手动写到上方 Identifier 'b' has already been declared.-标识符“ b”已被声明 // console.log(b) // Cannot access 'b' before initialization.初始化前无法访问“ b” // let b = 20; //不允许重复声明 // var a = 10; // var a = 20; // console.log(a); //a = 20 // // let b = 10; // let b = 20; // Identifier 'a' has already been declared // 标识符‘a’已被声明 // function fn(c){ // let c; //Identifier 'c' has already been declared. 标识符‘c’已被声明 // } //const一旦被声明,就无法再次被修改 ( 被声明同时需要立刻赋值,只声明不赋值就会报错 ) // const a; //Missing initializer in const declaration.const声明中缺少初始化程序 // const a = 10; // a = 20; //Assignment to constant variable.被分配到了不变的变量(常量) //const声明的对象同样不可以重复声明,直接修改常量对象,但是可以通过 对象名.对象属性 = '对象值' 进行修改和增加内部的属性和值 // const person = { // name:'x' // } // person.name = 'y'; // console.log(person.name) // 打印结果 'y' // // person.age = 'y'; // console.log(person)//打印结果 {name: "x", age: "y"} // //----------------------------------------------------- // const person = { // name:'x'; // } // person = { // name: 'y'//Assignment to constant variable.被分配到了不变的变量(常量) // } // person = { // age: 'y'//Assignment to constant variable.被分配到了不变的变量(常量) // } { //TDZ开始 a = 10; //不会报错 console.log(a); //a = 10; //TDZ结束 let a; //一旦在下方let a; 就会报错 //Cannot access 'a' before initialization(初始化前无法访问“ a”) a = 20; console.log(a) //a = 20; } //块级作用域 var a = 20; if (1 === 1) { var a = 40; } console.log(a); //打印结果 a = 40 //-------------------------------------- var a = 20; { let a = 40; } console.log(a); //打印结果 a = 20; var arr = [] var i;//var变量提升,从for循环被提到了上面 for(var i = 0; i < 10; i++){ arr[i] = function(){ return i; } } console.log(arr[5]());//打印结果为10 console.log(i) //打印结果为10 //用闭包的方法解决var变量提升 var arr = [] for(var i = 0; i < 10; i++){ arr[i] = (function(n){ return function(){ return n; } })(i) } const arr = [] for(let i = 0; i < 10; i++){ arr[i] = function(){ return i; } } console.log(arr[5]());//打印结果为5 console.log(i) //i is not defined //不会污染全局变量 var RegExp = 10; console.log(RegExp); //打印结果 10 console.log(window.RegExp); //打印结果 10 //------------------------------------------------ let RegExp = 10; console.log(RegExp); //打印结果 10 console.log(window.RegExp); //打印结果 ƒ RegExp() { [native code] } √√√√√√√√√√ //使用建议: 默认情况下使用const,只有在知道变量值会被修改的时候使用let声明变量 </script>
二、模板字符串
模板字符串(template string)是增强版的字符串,用反引号(`)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量
1
2
3
4
5
6
7
8
9
10
|
<script type= "text/javascript" > var imgSrc = './1.jpg' ; $( function () { $( 'ul' ).append(`<li> <a href= "javascript:void(0);" > <img src=${imgSrc} alt= "" > </a> </li>`) }); </script> |
三、箭头函数
- 如果参数只有一个,可以省略小括号
- 如果不写return,可以不写大括号
- 没有arguments变量
- 不改变this指向
- 其中箭头函数中this指向被固定化,不是因为箭头函数内部有绑定this的机制。实际原因是箭头函数根本没有自己的this,导致内部的this就是外层代码块的this。
- 箭头函数,this指向外层代码块的this
- 普通函数,this指向调用它的对象,也就是person对象
<script type="text/javascript"> // 函数的单体模式 fav(){ } // 等价于 普通函数 function fav() { } // 等价于 函数对象 var fav = function () { } //////////////////////////////////////////// // 普通函数 function add(a,b) { return a+b; }; alert(add(1,2)); // 函数对象 var add = function (a,b) { return a+b; }; alert(add(3,4)) // 箭头函数 var add = (a,b)=>{ return a+b; }; alert(add(3,7)) var f = a => a //等同于 var f = function(a){ return a; } //无形参 var f = () => 5; // 等同于 var f = function () { return 5 }; //多个形参 var sum = (num1, num2) => num1 + num2; // 等同于 var sum = function(num1, num2) { return num1 + num2; }; var person = { name:"alex", age: 20, fav:function () { // this指的是当前的对象,即使用它的对象 person对象 console.log(this.age); console.log(arguments[0]) } }; person.fav(); var person = { name:"alex", age: 20, fav: () => { // this的指向发生了改变,指向了定义时所在的对象window console.log(this); //window console.log(arguments) //报错 } }; person.fav(); //这个箭头函数的定义生效是在foo函数生成时,而它的真正执行要等到 100 毫秒后。如果是普通函数,执行时this应该指向全局对象window,这时应该输出21。但是,箭头函数导致this总是指向函数定义生效时所在的对象(本例是{id: 42}),所以输出的是42。 function foo() { setTimeout(() => { console.log('id:', this.id); }, 100); } var id = 21; foo.call({ id: 42 }); // id: 42 function foo() { setTimeout(function(){ console.log('id:', this.id); }, 100); } var id = 21; foo.call({ id: 42 }); // id: 21 </script>
<script type="text/javascript"> //带参数默认值的函数 function add(a, b) { return a + b; } console.log(add(30)); // NaN //ES5 function add(a, b) { a = a || 10; b = b || 20; return a + b; } console.log(add()); //打印结果 30 //ES6 function add(a = 10, b = 20) { return a + b; } console.log(add()); //打印结果 30 console.log(add(30)); //打印结果 50 console.log(add(30,60)); //打印结果 90 //参数默认值也可以是一个函数 function add(a, b = fn(5)) { return a + b; } function fn(val) { return val + 10; } console.log(add(30,2));//打印结果 32 console.log(add(30));//打印结果 45 //剩余参数 剩余运算符:把多个独立的参数合并到一个数组中 → 放在函数的传入形参 // 由三个点 ... 和一个紧跟着的具名参数指定 ...keys,剩余参数解决了最大的问题就是arguments问题 let book = { title: 'es6教程', year: 2019, price: 58 } function pick(obj) { let result = {}; for (let i = 1; i < arguments.length; i++) { // console.log(arguments); //打印结果0: {title: "es6教程", year: 2019, price: 58} 1: "title" 2: "year" //会打印出在0的整个对象,所以i必须从1开始遍历来跳过对book对象的遍历 result[arguments[i]] = obj[arguments[i]] } return result; } let bookData = pick(book, 'title', 'price');//传入不确定数量参数 console.log(bookData);//{title: "es6教程", price: 58} //es6剩余参数写法: 可以让传入的参数直接通过对剩余参数遍历,来获取对象内的属性 let book = { title: 'es6教程', year: 2019, price: 58 } function pick(obj, ...keys) { let result = {}; for (let i = 0; i < keys.length; i++) { result[keys[i]] = obj[keys[i]]; } return result; } let bookData = pick(book, 'title', 'price'); console.log(bookData);//{title: "es6教程", price: 58} /* * 扩展运算符 扩展运算符也是 ... 且只能放在最后一个参数,放在最前,放在中间,放两个都是错误的 把一个数组分割,并将各个项作为分离的参数传给函数 → 放到被调用的函数或者方法的()内 * */ const arr = [10, 42, 34, 7, 1, 65, 3, 63, 32]; const [a, b, ...c] = arr; console.log(c);//[34, 7, 1, 65, 3, 63, 32] //es5处理数组中的最大值,使用apply来获取 //apply:方法能劫持前面的对象,继承后面的对象的属性 apply(obj,args) console.log(Math.max.apply(null,arr)) //打印结果 65 //es6扩展运算符的使用更加方便 console.log(Math.max(...arr))//打印结果 65 let a = [1, 2, 3, 4, 5]; let b = [6, 7, 8, 9, 0]; let ab = [a, b];//把a和b放到ab数组中- 0:[1, 2, 3, 4, 5] 1:[6, 7, 8, 9, 0] let ab = [...a, ...b];//吧a和b两个数组合并-[1, 2, 3, 4, 5, 6, 7, 8, 9, 0] console.log(ab) let obj = { id: 3, name: 'Max', age: 23 } let { id, ...rest } = obj; console.log(id); //3 console.log(rest); //{name: "Max", age: 23} let obj1 = { id: 3, name: 'Max' } let obj2 = { age: 23, sex: 'man' } // var box = Objecte.assign({},obj1,obj2) 浅拷贝合并写法 let box = { ...obj1, ...obj2 }; console.log(box);//{id: 3, name: "Max", age: 23, sex: "man"} //如果两个对象有相同属性,后面的对象的属性值会覆盖前面的属性值 </script>
<script type="text/javascript"> //es6使用 => 来定义函数,() => {} 等同于 function(){} //es5写法 let add = function (a,b){ return a + b; } //es6写法 let add = (a,b)=> { return a + b; } console.log(add(10,20)) //箭头函数的简写 let fn = (a,b)=> { //两个参数这样写 return a + b; } //---------------------------------------------------------- let fn = a => { //只有一个参数括号可以省略 return a + 5; } //---------------------------------------------------------- let fn = (a,b) => a + b; let fn = a => a + 5; // 如果只是简单的函数运算,大括号可以省略 //---------------------------------------------------------- let fn = ()=> 'helow'+'world'; //如果没有传参,需要保留() console.log(fn()) //省略return返回对象时需要在{}外需要加 ( ),以区分函数{}格式,否则会报错 let getObj = a => { return { id:a, name:'Max' } } //--------------------------------------------------------------- let getObj = a => ({ id: a, name: 'Max' }); console.log(getObj(1))// 打印结果 {id: 1, name: "Max"} /* * 箭头函数的this指向 es5中的this指向,取决于调用该函数距离的最近对象 es6的箭头函数是没有this指向绑定的,箭头函数内部的this只能通过查找作用域链来确定,一旦使用箭头函数,当前就不存在作用域链,可以理解为this指向会跳过当前的调用对象向继续外层查找调用function函数的对象 * * */ let PageHandle = { id: 123, init: function () { document.addEventListener('click', function (event) { console.log(this);//这里的this指向了当前调用函数的document this.doSomeThings(event.type); }, false) }, doSomeThings: function (type) { console.log(`事件类型:${type},当前id:${this.id}`); } } //---------------------------------------------------------------------- let PageHandle = { id: 123, init: function () { document.addEventListener('click',function(event) { // this.doSomeThings is not a function // console.log(this); this.doSomeThings(event.type); }.bind(this),false) //bind解决this问题 }, doSomeThings:function(type){ console.log(`事件类型:${type},当前id:${this.id}`); } } PageHandle.init(); //---------------------------------------------------------------------- let PageHandle = { id: 123, init: function () { //函数对象声明的函数,不要使用箭头函数,否则会指向外层作用域 // 箭头函数没有this指向,箭头函数内部this值只能通过查找作用域链来确定,一旦使用箭头函数,当前就不存在作用域链 document.addEventListener('click', (event) => { console.log(this);//这里的this指向了PageHandle对象 this.doSomeThings(event.type); }, false) }, doSomeThings: function (type) { console.log(`事件类型:${type},当前id:${this.id}`); } } PageHandle.init() //---------------------------------------------------------------------- let PageHandle = { id: 123, init: ()=>{ // 箭头函数没有this指向,箭头函数内部this值只能通过查找作用域链来确定,一旦使用箭头函数,当前就不存在作用域链 document.addEventListener('click', (event) => { // this.doSomeThings is not a function console.log(this); //this 指向window this.doSomeThings(event.type); }, false) }, doSomeThings: function (type) { console.log(`事件类型:${type},当前id:${this.id}`); } } PageHandle.init(); /* * 使用箭头函数的注意事项 1.使用箭头函数,函数内部没有arguuments 2.箭头函数不能使用new关键字来实例化对象 * */ let getVal = function (a, b) { console.log(this);// window console.log(arguments);// 有arguments对象 return a + b; } let getVal = (a, b)=> { console.log(this);// window console.log(arguments);// arguments is not defined.没有arguments,调用就会报错 return a + b; } console.log(getVal(3, 6)); //--------------------------------------------------------- let Box = function () { } console.log(Box);// () => { } let a = new Box(); console.log(a);// Box{}__proto__: Object //------------------------------------------------------ let Box = () => { } console.log(Box);// () => { } let a = new Box(); console.log(a);//Box is not a constructor.Box不是构造函数 </script>
四、对象
1、属性简洁表示法
<script> // 属性简洁表示法 ES6允许直接写入变量和函数作为对象的属性和方法。 function(x,y){ return {x, y} } //上面的写法等同于: function(x,y){ return {x: x, y: y} } //对象的方法也可以使用简洁表示法: var o = { method(){ return "Hello!"; } }; //等同于: var o = { method: function(){ return "Hello!"; } } const name = 'Max', age = 23; const person = { name: name, age: age, sayName: function(){ } } //es中简写方式:属性名和引入的变量名一样,可以简写 const name = 'Max', age = 23; const person = { name, age, sayName(){ console.log(this.name) } } person.sayName();//Max const name = 'Max'; const obj = { isShow: true, [name + '-age']: 23, ['fn-' + name]() { console.log(this); } } console.log(obj);//{isShow: true, Max-age: 23, fn-Max: ƒ} function fn(x,y) { return {x,y}; } console.log(fn(10,20)); //{x: 10, y: 20} // let cart = { // wheel:4, // set(newVal){ // if(newVal < this.wheel){ // throw new Error('轮子数太少了') // } // this.wheel = newVal; // }, // get(){ // return this.wheel; // } // } // // console.log(cart.get()); // cart.set(6); // console.log(cart.get()) // 对象的方法 // is() === // 比较两个值是否严格相等 // console.log(NaN === NaN); console.log(Object.is(NaN,NaN)); // ***** assign() *** // 对象的合并 // Object.assign(target,obj1,obj2....) // 返回合并之后的新对象 let newObj = Object.assign({},{a:1},{b:2}); console.log(newObj); </script>
2、Object.assign()
- Object.assign方法用来将源对象(source)的所有可枚举属性复制到目标对象(target)。它至少需要两个对象作为参数,第一个参数是目标对象,第二个参数是源对象。
- 参数必须都是对象,否则抛出TypeError错误。
- Object.assjgn只复制自身属性,不可枚举属性(enumerable为false)和继承的属性不会被复制。
var x = {name: "Q1mi", age: 18}; var y = x; var z = Object.assign({}, x); x.age = 20; x.age // 20 y.age // 20 z.age // 18
<script type="text/javascript"> // 字面量方式创建对象 var person = { name:"alex", age: 20, fav:function () { console.log('喜欢'); // this指的是当前的对象 console.log(this.age); } }; person.fav(); // es6中对象的单体模式,为了解决箭头函数this指向的问题 推出来一种写法 var person = { name:"alex", age: 20, fav(){ // this指的是当前的对象,即使用它的对象 person对象,和普通函数一样的效果 console.log(this); console.log(arguments); } }; person.fav('21312'); // JavaScript 语言中,生成实例对象的传统方法是通过构造函数。 function Person(name,age){ this.name = name; this.age = age; } Person.prototype.showName = function(){ alert(this.name); }; // 使用new关机字来创建对象 var p = new Person('alex',19); p.showName() // ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。 class Person{ // 构造器 当你创建实例之后 constructor()方法会立刻调用 通常这个方法初始化对象的属性 // 这就是构造方法,而this关键字则代表实例对象。 constructor(name,age){ this.name = name; this.age = age; } //方法之间不需要逗号分隔,加了会报错。 showName(){ alert(this.name) } }; var p2 = new Person('张三',20); p2.showName(); </script>
<script> //ES5的构造对象的方式 使用构造函数来创造。构造函数唯一的不同是函数名首字母要大写。 function Point(x, y){ this.x = x; this.y = y; } // 给父级绑定方法 Point.prototype.toSting = function(){ return '(' + this.x + ',' + this.y + ')'; }; var p = new Point(10, 20); console.log(p.x); //10 p.toSting(); // 继承 function ColorPoint(x, y, color){ Point.call(this, x, y); this.color = color; } // 继承父类的方法 ColorPoint.prototype = Object.create(Point.prototype); // 修复 constructor ColorPoint.prototype.constructor = Point; // 扩展方法 ColorPoint.prototype.showColor = function(){ console.log('My color is ' + this.color); }; var cp = new ColorPoint(10, 20, "red"); console.log(cp.x); // 10 console.log(cp.toSting()); //(10,20) cp.showColor(); //My color is red //ES6 使用Class构造对象的方式: class Point{ constructor(x, y){ this.x = x; this.y = y; } // 不要加逗号 toSting(){ return `(${this.x}, ${this.y})`; } } var p = new Point(10, 20); console.log(p.x); p.toSting(); class ColorPoint extends Point{ constructor(x, y, color){ super(x, y); // 调用父类的constructor(x, y) this.color = color; } // 不要加逗号 showColor(){ console.log('My color is ' + this.color); } } var cp = new ColorPoint(10, 20, "red"); console.log(cp.x); cp.toSting(); cp.showColor() </script>
五、类
ES6 引入了关键字class来定义一个类,constructor是构造方法,this代表实例对象。
类之间通过extends继承,继承父类的所有属性和方法。
super关键字,它代指父类的this对象,子类必须在constructor中调用super()方法,
否则新建实例时会报错,因为子类没有自己的this对象。调用super()得到this,才能进行修改。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
<script type= "text/javascript" > class Animal{ constructor(){ this .type = "animal" } says(say){ console.log( this .type + "says" + say ) } } let animal = new Animal() animal.says( "hello" ) class Dog extends Animal{ constructor(){ super (); this .type = "dog" ; } } let dog = new Dog() dog.says( "hi" ) </script> |
六、import和export
import 导入模块、export导出模块
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
// main.js // 导出多个声明 export var name = "alex" export var age = "18" export function aa() { return 1 } // 批量导出 export {name, age, aa} // test.js import {name, age, aa} from "./main" console.log(name) console.log(age) console.log(aa()) // 整个模块导入 把模块当做一个对象 // 该模块下所有的导出都会作为对象的属性存在 import * as obj from "./main" console.log(obj.name) console.log(obj.age) console.log(obj.aa()) // 一个模块只能有一个默认导出 // 对于默认导出 导入的时候名字可以不一样 // main.js var app = new Vue({ }); export default app // test.js // import app from "./main" import my_app from "./main" |
七、解构赋值
ES6允许按照一定的模式,从数组或对象中提取值,对变量进行赋值,这种方式被称为解构赋值。
<script type="text/javascript"> //解构赋值是对赋值运算符的一种扩展 ,它通常针对数组对象来进行操作,优点是简洁易读 //数组解构赋值 let [a,b,c] = [1,2,3]; let [b] = []; let [a,b] = [1]; console.log(b) // undefined //对象解构赋值 let node = { type:'identifier', name:'foo' } //es5写法 var type = node.type; var name = node.name; //es6对象解构写法 let {type,name} = node; console.log(type,name)// identifier foo //使用剩余运算符解构出来的是 对象 而不是 数组 let obj = { a: 'Max', b: [], c: 'hellow world' } // let { a } = obj;//可忽略b,c的属性进行解构 let { a, ...x } = obj;//使用剩余运算符 将a之后的属性赋值到一个新的对象中 console.log(a);// Max console.log(x);//{b: Array(0), c: "hellow world"} console.log(x.c);//hellow world //提取json数据 let jsonData = { id: 42, status: 'ok', data: [867, 6543] } let { id, status } = jsonData; //{}内只需要放入需要提取的属性名 console.log(id); //42 let { data: number } = jsonData; //如果需要对jsonData内的属性进行重新取名只需要这样写,就可以给:后面的属性赋上该属性的值 console.log(number); //[867, 6543] //模块的指定方法 const {getJson,getBoolean} = require('xxx'); //函数返回一个数组,通过函数解构赋值 function fn1() { let a = 1, b = 2, c = 3; return [a, b, c]; } let [a, b, c] = fn1(); console.log(a); //1 console.log(b); //2 console.log(c); //3 //------------------------------------------------- //函数返回一个对象,通过函数解构赋值 function fn2() { let x = 1, y = 2, z = 3; return { x: x, y: y, z: z } } let { x, y, z } = fn2() console.log(x); //1 console.log(y); //2 console.log(z); //3 //参数是一组有次序的数组 let arr = [1, 2, 3]; let q = 3, w = 4; function fn1(q, [x, y, z], w) { return q + x + y + z + w; } console.log(fn1(q, arr, w));//13 //参数是一个没有顺序的对象 let obj = { a: 1, b: 2, c: 3 }; let q = 3, w = 4; function fn2(q, { b, a, c }, w) { return q + a + b + c + w; } console.log(fn2(q, obj, w));//13 </script>
八、字符串函数
- 在此之前,JavaScript中只有str.indexOf(char)方法可用来确定一个字符串是否包含在另一个字符串中。
- ES6中又提供了3种新方法:
- includes():返回布尔值,表示是否找到了参数字符串。
- stratsWith():返回布尔值,表示参数字符串是否在源字符串的开始位置。
- endsWith():返回布尔值,表示参数字符串是否在源字符串的结尾位置。
<script> var s = "Hello world!"; s.includes("o"); // true s.startsWith("Hello"); // true s.endsWith("!"); // true //这三个方法都支持第2个参数,表示开始匹配的位置。 s.includes("o", 8); // false s.startsWith("world", 6); // true s.endsWith("Hello", 5); // true </script>
九、Promise
- Promise 是异步编程的一种解决方案,比传统的解决方案(回调函数和事件)更合理、更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了
Promise
对象。 - 使用Promise的优势是有了
Promise
对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise
对象提供统一的接口,使得控制异步操作更加容易。 Promise
构造函数接受一个函数作为参数,该函数的两个参数分别是resolve
和reject
。它们是两个函数,由 JavaScript 引擎提供,不用自己部署。Promise
实例生成以后,可以用then
方法分别指定resolved
状态和rejected
状态的回调函数。then
方法可以接受两个回调函数作为参数。第一个回调函数是Promise
对象的状态变为resolved
时调用,第二个回调函数是Promise
对象的状态变为rejected
时调用。其中,第二个函数是可选的,不一定要提供。这两个函数都接受Promise
对象传出的值作为参数。其实Promise.prototype.catch
方法是.then(null, rejection)
的别名,用于指定发生错误时的回调函数。
const promiseObj = new Promise(function(resolve, reject) { // ... some code if (/* 异步操作成功 */){ resolve(value); } else { reject(error); } }); <script> promiseObj.then(function(value) { // success }, function(error) { // failure }); //还可以将上面的代码写成下面这种方式: promiseObj .then(function(value) { // success }) .catch(function(error) { // failure }); </script>
十、其他
<script> /* * 原始数据类型Symbol,它表示是独一无二的值,用的很少,只做了解 最大的用途:用来定义对象的私有变量 * */ const name1 = Symbol('name'); const name2 = Symbol('name'); console.log(name === name); // false let s1 = Symbol('s1');//声明一个Symbol变量 console.log(s1);//Symbol(s1) let obj = { [s1]: 'Max' //[变量名]将Symbol变量引入对象 }; console.log(obj);//{Symbol(s1): "Max"} //如果用Symbol定义的对象中的变量,取值时一定要用[变量名],用obj.s1 会返回 undefined console.log(obj[s1]);// Max //Symbol变量无法被遍历,且不易被获取 let s = Object.getOwnPropertySymbols(obj); console.log(s);//[Symbol(s1)] console.log(s[0]);//Symbol(s1) let l = Reflect.ownKeys(obj); console.log(l);//[Symbol(s1)] console.log(l[0]);//Symbol(s1) </script>
<script> /* Set集合表示无重复值的有序列表 * */ let set = new Set(); console.log(set);//Set(0) {} //添加元素 set.add(2); set.add('4'); set.add('4'); set.add([1, 2, 3]) console.log(set);//Set(3) {2, "4", Array(3)} //删除元素 set.delete(2); console.log(set);//Set(2) {"4", Array(3)} //校验元素 console.log(set.has('4'));//true //访问集合的长度 console.log(set.size);//2 //将set集合转换成数组 let set2 = new Set([1, 2, 3, 3, 3, 3, 4]); let arr = [...set2];//通过扩展运算符操作 console.log(arr);//(4) [1, 2, 3, 4] // 1.set中对象的引用无法被释放 // let set3 = new Set(),obj = {}; // set3.add(obj); // // 释放当前的资源 // obj = null; // console.log(set3); let set4 = new WeakSet(), obj = {}; set4.add(obj); // 释放当前的资源 obj = null; console.log(set4); // WeakSet // 1.不能传入非对象类型的参数 // 2.不可迭代 // 3.没有forEach() // 4.没有size属性 //Map类型是键值成对的有序列表,键和值是任意类型 let map = new Map(); //设置键和值 map.set('name', 'Max');//前面是键 后面是值 map.set('age', '23'); console.log(map);//Map(2) {"name" => "Max", "age" => "23"} //获取值 console.log(map.get('name'));//Max //校验属性 console.log(map.has('name'));//true //删除属性 map.delete('name'); console.log(map);//Map(1) {"age" => "23"} //清除属性 map.clear(); console.log(map);//Map(0) {} //键和值可以是任意类型 map.set(['a', [1, 2, 3]], 'hello'); console.log(map);//Map(1) {Array(2) => "hello"} let m = new Map([ ['a', 1], ['c', 2] ]); console.log(m); //Map(2) {"a" => 1, "c" => 2} </script>
<script> //from() 将伪数组转换成真正的数组 function add() { console.log(arguments); //es5转换 let arr1 = [].slice.call(arguments) console.log(arr1);//[1, 2, 3] //es6写法 let arr2 = Array.from(arguments) console.log(arr2);//[1, 2, 3] } add(1, 2, 3) /* <ul> <li>1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li> </ul> */ let lis1 = Array.from(document.querySelectorAll('li')); console.log(lis1);//(5) [li, li, li, li, li] //或者用扩展运算符将伪数组转换成真正的数组 let [...lis2] = document.querySelectorAll('li'); console.log(lis2);//(5) [li, li, li, li, li] //from() 还可以接收第二个参数,用来对每个元素进行处理 let lis = document.querySelectorAll('li'); let liContents = Array.from(lis, ele => ele.textContent); //少声明一个变量,合并写成↓↓↓↓↓↓↓↓↓ let liContents = Array.from(document.querySelectorAll('li'), ele => ele.textContent); console.log(liContents);//(5) ["1", "2", "3", "4", "5"] //of() 将任意的数据类型转换成数组 console.log(Array.of(3, 11, 32, '52', [23, 4, 1, 3], { id: 1, name: 'Max' })); // (6) [3, 11, 32, "52", Array(4), {…}] //copyWithin() 将数组内部指定位置的元素覆盖到其他的位置,返回当前数组 - 用途不大只做了解 /* * copyWithin(target, start = 0, end = this.length) target:目的起始位置。 start:复制源的起始位置,可以省略,可以是负数。 end:复制源的结束位置,可以省略,可以是负数,实际结束位置是end-1。 * */ console.log([1, 2, 4, 5, 8, 10].copyWithin(0, 2, 4)) //[4, 5, 4, 5, 8, 10] //findIndex() 找出第一个符合条件的数组成员的索引 let arr = [1, 4, 5, 3, 5, 56, 3, -3, 32, 7]; let num = arr.find(n => n < 0);//find括号内是个箭头回调函数 console.log(num);//7 //数组的方法 keys() / values() / entries() & next() //返回一个遍历器,可以使用 for( 变量 of 对象.遍历器 ) 进行循环遍历 let arr = ['a', 'b', 'c', 'd', 'e']; for (let index of arr.keys()) { //对键名进行遍历 console.log(index); // 0 1 2 3 4 }; //---------------------------------------------------------- for (let index of arr.value()) { //对值进行遍历 console.log(index);// a b c d e }; //---------------------------------------------------------- for (let [index, ele] of arr.entries()) { //对键值对进行遍历 console.log(index, ele);// 0"a" 1"b" 2"c" 3"d" 4"e" }; //next() 表示下一次调用,进行单次遍历,超出范围会返回undefined let arr = ['a', 'b', 'c']; let it = arr.entries(); console.log(it.next().value); //(2) [0, "a"] console.log(it.next().value); //(2) [1, "b"] console.log(it.next().value); //(2) [2, "c"] console.log(it.next().value); //undefined //数组的方法 includes() //返回一个布尔值,表示某个数组是否包含给定的值,可以直接用if判断语句 let arr = ['a', 'b', 'c']; console.log(arr.includes('a')); //true console.log(arr.includes('e')); //false </script>
<script> // Iterator // 是一种新的遍历机制,两个核心 // 1.迭代器是一个接口,能快捷的访问数据,通过Symbol.iterator来创建迭代器 通过迭代器的next()获取迭代之后的结果 // 2.迭代器是用于遍历数据结构的指针(数据库的游标) // 使用迭代 const items = ['one', 'two', 'three']; // 1.创建新的迭代器 const ite = items[Symbol.iterator](); console.log(ite);//Array Iterator {} console.log(ite.next()); //{value: "one", done: false} done如果为false表示遍历继续 如果为true表示遍历完成 console.log(ite.next());//{value: "tow", done: false} console.log(ite.next());//{value: "three", done: false} console.log(ite.next());//{value: undefined, done: false},true表示遍历结束并返回undefined /* function后面,函数名之前有个 * 表示这是一个生成器函数 只能在函数内部使用 yield 表达式让函数挂起 generator函数 可以通过yield关键字,将函数挂起,为了改变执行流提供了可能,同时为了做异步编程提供了方案 它普通函数的区别 1.function后面 函数名之前有个* 2.只能在函数内部使用yield表达式,让函数挂起 总结:generator函数是分段执行的,yield语句是暂停执行 而next()恢复执行 */ function* fn() { let a = 1, b = 2, c = 3; console.log('frist'); yield a; console.log('second'); yield b; console.log('third'); yield c; console.log('end'); } let fnObj = fn(); console.log(fnObj);//fn {<suspended>} 声明生成器变量,并返回函数状态 - 暂停 console.log(fnObj.next());// frist {value: 1, done: false} console.log(fnObj);//fn {<suspended>} console.log(fnObj.next());// second {value: 2, done: false} console.log(fnObj.next());// third {value: 3, done: false} console.log(fnObj.next());// end {value: undefined, done: true} console.log(fnObj);//fn {<closed>} 执行完毕后的生成器对象,返回函数状态 - 关闭 function* fn() { let a = 1, b = 2, c = 3; console.log('frist'); let x = yield a; console.log('second'); let y = yield b + x; console.log('third'); let z = yield c + y; console.log('end'); return x + y + z + a + b + c; } let fnObj = fn(); console.log(fnObj);//fn {<suspended>} console.log(fnObj.next());// frist {value: 1, done: false} console.log(fnObj.next(10));// second {value: 12, done: false} console.log(fnObj.next(20));// third {value: 23, done: false} console.log(fnObj.next(30));// end {value: 66, done: true} //Generator() 为不具备Interator接口的对象提供了遍历操作 function* objectEntries(obj) { const propKeys = Object.keys(obj);//获取对象的所有key保存到数组[name,age] for (const propKey of propKeys) { yield [propKey, obj[propKey]]//for循环[键,对象[键] = 值] } } const obj = { id: 1, name: 'Max', age: 23, } obj[Symbol.iterator] = objectEntries;//***通过Symbol.iterator让对象具有迭代器的接口,可以进行遍历操作 console.log(obj);//{id: 1, name: "Max", age: 23, Symbol(Symbol.iterator): ƒ} for (let [key, value] of objectEntries(obj)) {//调用生成器函数并传出obj对象,for循环遍历出键和值 console.log(`${key}:${value}`); } //应用: Generator()部署ajax异步操作,让异步代码同步化 //加载loading... //数据加载完成(异步操作) //隐藏loading... function loadUI() { console.log('加载loading...动画'); } function showData() { setTimeout(() => { //用setTimeout模拟异步操作 console.log('加载完成'); itLoad.next();//4-数据返回之后调用next()继续执行生成器下一步 }, 1000); } function hideUI() { console.log('隐藏loading...动画'); } //生成器函数,让异步操作同步化 function* load() { loadUI(); yield showData();//3-运行到这里挂起并调用showData()函数 hideUI();//5-隐藏loading... } let itLoad = load();//1-调用生成器函数声明迭代器对象,生成器函数被挂起 itLoad.next();//2-首次运行生成器函数 //ajax 调用 // Generator 部署ajax操作,让异步代码同步化 // 回调地狱 /* $.ajax({ url: 'https://free-api.heweather.net/s6/weather/now?location=beijing&key=4693ff5ea653469f8bb0c29638035976', method: 'get', success(res) { console.log(res); // 继续发送请求 $.ajax({ url: '', method: 'get', success(res1) { // 发送ajax $.ajax({ url: '', method: 'get', success(res2) { // 发送ajax $ } }) } }) } }) */ //声明需要进行同步操作的生成器函数 function* main() { console.log('开始运行'); let res = yield request('https://free-api.heweather.net/s6/weather/now?location=beijing&key=4693ff5ea653469f8bb0c29638035976'); //3-遇到yield语句,挂起并执行request函数,传入api接口 console.log(res); //后面的操作 console.log('数据请求完成,可以继续操作'); } //声明ajax调用函数 function request(url) { //4-接收api接口地址 $.ajax({ url,//5-api地址(url:url,接收传入的api参数) method: 'get',//6-get方法调用 success(res) { ite.next(res);//把res的值返回给生成器,让生成器继续执行,打印api的返回值 } }) } const ite = main();//1-调用生成器函数声明迭代器对象,生成器函数被挂起 ite.next();//2-首次执行生成器函数 </script>
<script> /* * 所谓 Promise,就是一个对象,用来传递异步操作的消息。它相当于一个容器,存放了了某个未来才会知道结果的事件(通常是一个异步操作),并且这个事件提供统一的 API(resolve 和 reject),用来返回成功或者失败的结果,可供then等方法对成功或失败进行对应的操作 Promise对象的两个特点: 对象的状态不受外界影响,处理异步操作有三个状态,Pending(进行) Resolved(已完成) Rejected(已失败) 只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是 Promise 这个名字的由来,它的英语意思就是「承诺」,表示其他手段无法改变 一旦状态改变,就不会再被修改,任何时候都只会得到这个结果 Promise 对象的状态改变,只有两种可能:从 Pending 变为 Resolved 和从 Pending 变为 Rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。就算改变已经发生了,你再对 Promise 对象添加回调函数,也会立即得到这个结果 有了 Promise 对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise 对象提供统一的接口,使得控制异步操作更加容易 Promise对象的缺点: 无法取消 Promise,一旦新建它就会立即执行,无法中途取消 如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部 当处于 Pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成) Promise 构造函数接收一个回调函数作为参数,该函数的两个参数分别是 resolve函数和 reject函数 这个回调函数接收两个参数,分别为promise的resolve(成功) rejecte(失败) 两个函数 then() 方法来接收通过if判断返回的结果,第一个参数是resolve回调函数,第二个是可选的,是rejecte状态回调函数,then() 返回一个新的promise实例,可以采用链式编程 如果异步操作成功,则用 resolve 方法将 Promise 对象的状态,从「未完成」变为「成功」(即从 pending 变为 resolved) 如果异步操作失败,则用 reject 方法将 Promise 对象的状态,从「未完成」变为「失败」(即从 pending 变为 rejected) * */ var promise = new Promise(function (resolve, reject) {//声明promise对象,并立即执行 if ('异步操作成功') { resolve(value); } else { reject(error); } }); console.log(promise); //Promise {<pending>} 无需then方法调用,就返回了一个正在进行状态的Promise对象 promise.then(function (value) { // success }, function (value) { // failure }); //理解promise执行顺序 let promise = new Promise(function (resolve, rejecte) { //1.声明promise对象,并立即执行 let res = { //声明res对象,模拟从后端返回的数据 code: 200, data: { name: 'Max', age: 23 }, error: '失败了' } setTimeout(() => { //用setTimeout模拟后端返回数据的时间 if (res.code === 200) { //2.条件判断code值是否符合条件 resolve(res.data); //3-①符合条件,执行resolve函数中的-获得data并返回给then方法的第一个函数 } else { rejecte(res.error);//3-②不符合条件,执行rejecte函数中的-获得误信息并返回给then的第二个函数 } }, 1000); }) //4.调用promise的then方法接收回调 promise.then((val) => { //5-①then方法通过Val接收resolve传入的值 console.log(val);//6-①在函数内进行处理,本处打印(res.data) }, (err) => { //5-②then方法通过err接收rejecte传入的值 console.log(err);//6-②在函数内进行处理,本处打印(res.error) }) /* * 封装Promise 为避免Promise一旦被声明立即执行的特性,以及解决不能传参的问题,就需要对Promise进行函数封装,通过这个函数对Promise进行链式调用并传参,只有调用这个函数的时候,才返回一个Promise实例化对象,并开始执行,调用函数的同时,使用then方法来接收Promise返回的结果 * */ function timeOut(ms) {//2.接收传入的参数 return new Promise((resolve, rejecte) => {//3.开始执行Promise函数 setTimeout(() => { resolve('Promise Success!!')//4.通过rosolve函数返回给then一个结果 }, ms); }) } //1.函数调用,传入参数,并返回一个Promise实例化对象 timeOut(1000).then((val) => {//5.then方法通过val接收传入的结果,并执行内部函数 console.log(val);//6.打印传入的val的值 }) //使用Promise封装ajax const getJSON = function (url) { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest();//创建一个 `XMLHttpRequest` 对象 xhr.open('GET', url);//打开xhr,GET方法打开传入的url xhr.onreadystatechange = handler;//调用当前状态发生改变的回调函数 xhr.responseType = 'json';//约定返回数据的类型 xhr.setRequestHeader('Accept', 'application/json');//设置请求头 xhr.send();//发送 function handler() { //创建对当前状态判断的回调函数 if (this.readyState === 4) { if (this.status === 201) { resolve(this.response.HeWeather6[0].now); } else { reject(new Error(this.statusText)) } } } }) } getJSON('https://free-api.heweather.net/s6/weather/now?location=beijing&key=4693ff5ea653469f8bb0c29638035976') .then(data => { console.log(data); }).catch(err => { console.log(err); }) //Promise对象的其他方法 // then()方法 // then() 第一个参数是relove回调函数,第二个参数是可选的 是reject状态回调的函数 // then()返回一个新的promise实例,可以采用链式编程 // resolve() reject() all() race() done() finally() /* * * resolve() 能将现有对象直接转换为一个Promise对象 转换完成之后这个对象就有resolve()独有的状态,就可以通过 then() catch() 去获取 let p = new Promise(resolve => {//下方等价于这个 return resolve('foo') }) //------------------------------------------------- let p = Promise.resolve('foo'); console.log(p) //Promise {<resolved>: "foo"} p.then(data => { console.log(data);//foo }) rejecte()能将先有对象转换为一个Promise对象 同resolve() all() 提供了异步并行的操作行为,多个Promise同时进行,只有全部Promise都返回resolve才算成功 让then一次接收Promise返回结果的方法,让所有异步操作都执行完成,没有错误之后再调用对应的方法 let promise1 = new Promise((resolve,rejecte)=>{}); let promise2 = new Promise((resolve,rejecte)=>{}); let promise3 = new Promise((resolve,rejecte)=>{}); let proAll = Promise.all([promise1,promise2,promise3]);//注意所有Promise对象要放大数组内 proAll.then(()=>{ //三个都成功才算成功,再执行此处方法 }).catch(err=>{ //其中一个失败则失败,执行失败的方法 }) 一些游戏类的素材加载用的比较多,等待图片、flash、静态资源文件,等待所有页面文件都加载完成后,才进行页面的初始化 race() 同样是将多个Promise对象,包装成一个新的Promise对象,其中一个完成就立即改变状态,并通过then等方法接收返回结果 第一个结束的Promise的返回结果,可以是resolve或rejecte,来传递给then或catch进行下一步的操作 应用:请求一个图片资源,超过3秒停止加载,并返回timeout的rejecte结果,3秒之内返回图片就进入then方法,超过3秒请求失败,就会进入catch方法 function requestImg(imgSrc) { return new Promise((resolve, rejecte) => { const img = new Image(); img.onload = function () { resolve(img); } img.src = imgSrc; }) } function timeout() { return new Promise((resolve, rejecte) => { setTimeout(() => { rejecte(new Error('图片请求超时')); }, 3000); }) } let requestTimeout = Promise.race([requestImg('http://pic.netbian.com/downpic.php?id=18363&classid=66'), timeout()]); requestTimeout.then(data => { document.body.appendChild(data); }).catch(err => { console.log(err); }) server.listen(3000).then(()=>{ }).finally(server.stop()); 需要注意的是这里 请求图片 和 超时 的两个方法都需要放到数组中 done() 用于接收错误 Promise 对象的回调链,不管以then方法或catch方法结尾,要是最后一个方法抛出错误,都有可能无法捕捉到(因为 Promise 内部的错误不会冒泡到全局)。因此,我们可以提供一个done方法,总是处于回调链的尾端,保证抛出任何可能出现的错误 finally() 在任何情况下都会执行,用于完成Promise状态返回后无论成功失败都必须执行的操作 finally方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。它与done方法的最大区别,它接受一个普通的回调函数作为参数,该函数不管怎样都必须执行 * */ </script>
<script> /* //Generator Promise async 1.解决回调地域 2.使得异步操作显得更加方便 async作用 使得异步操作更加方便,它会返回一个Promise对象,并用 then() catch() 接收返回结果 async是Generator的语法糖,使之更加易于阅读 Error.name的六种值对应的信息: TypeError:操作数据类型错误 EvalError:eval()的使用与定义不一致 RangeError:数值越界 ReferenceError:非法或不能识别的引用数值 SyntaxError:发生语法解析错误 URIError:URI处理函数使用不当 */ async function fn() {//假设这里需要向服务器多次请求 let str = await 'hellow async'; let data = await str.split('');//await的作用就是让上一次请求返回结果后,再进行这一步操作 return data;//最终需要return将结果返回出去 } //如果async函数中有多个await,那么then函数会等待所有的await指令运行完毕后,接收返回的结果并去执行相应的操作 fn().then(v => { console.log(v);//(11) ["h", "e", "l", "l", "w", " ", "a", "s", "y", "n", "c"] }).catch(e => console.log(e)); async function f2() { // throw new Error('出错了'); try { await Promise.reject('出错了'); } catch (error) { } return await Promise.resolve('hello'); } f2().then(v => console.log(v)).catch(e => console.log(e)); // 需求: 想获取和风天气 现在now的数据 //async调取Promise封装的函数,通过then获取返回结果 const getJSON = function (url) { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.open('GET', url); xhr.onreadystatechange = handler; xhr.responseType = 'json'; xhr.setRequestHeader('Accept', 'application/json'); // 发送 xhr.send(); function handler() { if (this.readyState === 4) { if (this.status === 200) { resolve(this.response); } else { reject(new Error(this.statusText)); } } } }) } async function getNowWeather(url) { // 发送ajax 获取实况天气 let res = await getJSON(url); console.log(res); // 获取HeWeather6的数据 获取未来3~7天的天气状况 let arr = await res.HeWeather6; return arr[0].now; } getNowWeather( 'https://free-api.heweather.net/s6/weather/now?location=beijing&key=4693ff5ea653469f8bb0c29638035976') .then(now => { console.log(now); }) </script>
<script> //es5造类 function Person(name, age) { this.name = name; this.age = age; } Person.prototype.sayName = function () { return this.name; } Person.prototype.sayAge = function () { return this.age; } let p1 = new Person('Max', 23); console.log(p1); //Person {name: "Max", age: 23} console.log(p1.sayName()); //Max console.log(p1.sayAge()); //23 //es6造类 class Person { constructor(name, age) { this.name = name; this.age = age; } sayName() { return this.name; } sayAge() { return this.age; } } let p1 = new Person('Max', 23); console.log(p1); //Person {name: "Max", age: 23} console.log(p1.sayName()); //Max console.log(p1.sayAge()); //23 //或者通过Object.assign()方法一次性向类中添加多个放大 Object.assign(Person.prototype, { sayName(){ return this.name; }, sayAge(){ return this.age; }, }) //使用关键字 extends 来让子类继承父类的方法,然后通过super调用父类中的constructor方法 class Animal { constructor(name, age) { this.name = name; this.age = age; } sayName() { return this.name; } sayAge() { return this.age; } } class Dog extends Animal { constructor(name, age, kind) { super(name, age); //super是超类的意思,它会去调用父类中的contructor方法 // Animal.call(this, mame, age); this.kind = kind; } //子类自己的方法 sayInfo() { return `${this.name}已经${this.age}岁了,是条${this.kind}。` } //重写父类的方法 - 子类的方法与父类同名就会覆盖父类的方法 sayName() { return this.name + super.sayAge() + this.kind; } } let dog1 = new Dog('小黄', 4, '秋田犬'); console.log(dog1.sayInfo());//小黄已经4岁了,是条秋田犬。 console.log(dog1.sayName());//小黄4秋田犬 /* * Mixin 混入模式的实现* Mixin 指的是多个对象合成一个新的对象,新对象具有各个组成成员的接口 它的最简单实现如下,c对象是a对象和b对象的合成,具有两者的接口 * */ const a = { a: 'a' }; const b = { b: 'b' }; const c = {...a, ...b}; //代码的mix函数,可以将多个对象合成为一个类,使用的时候,只要继承这个类即可 //声明mix函数,通过调用拷贝函数,来拷贝类的全部属性并返回,用剩余参数来接收不定个数的类来传参 function mix(...mixins) { class Mix { constructor() { for (let mixin of mixins) { copyProperties(this, new mixin()); // 拷贝实例属性 } } } for (let mixin of mixins) { copyProperties(Mix, mixin); // 拷贝静态属性 copyProperties(Mix.prototype, mixin.prototype); // 拷贝原型属性 } return Mix; } //声明一个拷贝的函数 function copyProperties(target, source) { for (let key of Reflect.ownKeys(source)) { if (key !== 'constructor' && key !== 'prototype' && key !== 'name' ) { let desc = Object.getOwnPropertyDescriptor(source, key); Object.defineProperty(target, key, desc); } } } //声明一个新的类,来继承并调用mix函数,传入需要混入的类,在mix函数中进行拷贝并接收返回的全部属性 class DistributedEdit extends mix(class1, class2, class3) { // ... } </script>
<script> /* * es6模块化概念 在es6之前js对于模块化的支持不是很友好,主要通过 CommonJS(服务器端) 和 CMD/AMD(浏览器端)来实现,es6在语言标准的层面实现了模块功能,而且相当简单,可以完全取代以上两个,当前es6的module已经成为了浏览器和服务器通用的模块的解决方案,非常实用 es6模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系以及我们输入输出的变量到底是什么 es6的模块化命令 es6模块功能主要有两个命令构成: export : 抛出,用于规定模块的对外接口 import : 引入,用于输入其他模块提供的功能 一个模块就是一个独立的文件 export:需要抛出的对象,在前面加上export,或者使用 export default xxx; import:在浏览器模块中,需要给当前的script标签加一个类型module import * as xxx from './xxx.js'; 是一次性接收组件内抛出的所有对象, 然后在引入的文件内需要调用的地方以 xxx. 为关键字引入对象,例如 index.name , 通过 export default 抛出的对象则需要用 index.default 接收 * */ //----------modules/index.js export const name = 'Max'; export const age = '23'; export function sayName() { return `请叫我${name}`; } // 或者 // function sayName() { // return `请叫我${name}`; // } // export {sayName} // 不能是 export sayName 要用对象包裹起来 export const obj = { foo: 'foo' } class Person { constructor() { } sayAge() { console.log(age); } } export default Person;//export default抛出,在一个组件中只能使用一次,收的时候放在{}外 </script> <script type='module'> //加上type属性 // CommonJS和AMD // ES6 module import Person, { name, age, sayName, obj } from './modules/index.js';//把export的对象放入{}内 // import * as index from './index.js'; console.log(name);//Max console.log(age);//23 console.log(sayName());//请叫我Max console.log(obj);//{foo: "foo"} console.log(Person()) const p = new Person;//实例化Person,就可以通过实例化对象调用Person的方法 p.sayAge();//23 </script>