ES6核心,值得驻足花一天时间来学习
栏目: JavaScript · 发布时间: 6年前
内容简介:在es5时,只有两种变量声明,var 和function。在es6中新增了四种let和const,以及另外两种声明import和class。 我们先讲解let和const,后续会补充import和class我们先来看基本语法我们在代码块中声明了a,b。然后a is not defined.这是因为let命令只在对应的代码块中有效,我们在外部去引用它,就会报错。这就是let的块级作用域的效果,如果不太清楚什么是块级作用域。我们来看下面的例子
在es5时,只有两种变量声明,var 和function。在es6中新增了四种let和const,以及另外两种声明import和class。 我们先讲解let和const,后续会补充import和class
(1)let
我们先来看基本语法
{ let a = 10; var b = 1; } b // 1 a // ReferenceError: a is not defined. 复制代码
我们在代码块中声明了a,b。然后a is not defined.这是因为let命令只在对应的代码块中有效,我们在外部去引用它,就会报错。这就是let的块级作用域的效果,如果不太清楚什么是块级作用域。我们来看下面的例子
var a = []; for (var i = 0; i < 10; i++) { a[i] = function () { console.log(i); }; } a[0]();//10 a[6](); // 10 复制代码
这是一个老生常谈的问题了。i在for循环中定义的是全局变量。我们在调用函数时。函数内部的i引用的是全局的i,所以打印出来的 是10. 我们之前是这样解决的。
var a = []; for (var i = 0; i < 10; i++) { a[i] = (function (a) { return function(){ console.log(a); } }(i)); } a[0]();//0 a[6](); // 6 复制代码
我们使用了立即执行函数将i的值传入了内部的函数,这样内部的函数就能够获取到对应的i。
我们用let来代替var,最后输出的是 6。
var a = []; for (let i = 0; i < 10; i++) { a[i] = function () { console.log(i); }; } a[6](); // 6 复制代码
这是因为我们每一次的循环都生成了一个新的块级作用域,内部保存着i的值,所以就会打印出6.
let不存在变量提升
console.log(a); console.log(b); var a=0;//undefined let b=0;//ReferenceError: b is not defined 复制代码
TDZ(暂时性死区) let命令在块级作用域中,即使不存在变量提升,它也会影响当前块级作用域,即绑定在了当前作用域。在作用域中引用外部的变量将会报错。
var a=10; { console.log(a); //ReferenceError: a is not defined let a=10; } 复制代码
同时,我们在TDZ中使用typeof也会报错.
console.log( typeof a);//undefined 复制代码
console.log( typeof a);//ReferenceError: a is not defined let a=10; 复制代码
let 不允许重复声明变量
{ var a=0; let a=1;//SyntaxError: Identifier 'a' has already been declared } 复制代码
(2) const常量
const常量声明一个只读属性的变量,不可更改,不可先声明后赋值,生成块级作用域。
const a;//SyntaxError: Missing initializer in const declaration(const声明中缺少初始值设定项) a=10; 复制代码
它同let有很多相似的地方。 .不可重复声明
const a=10; var a=10;//SyntaxError: Identifier 'a' has already been declared 复制代码
.变量不提升
console.log(a)//ReferenceError: a is not defined const a=10; 复制代码
.同样存在暂时性死区
var a=10; { console.log(a);//ReferenceError: a is not defined const a=10; } 复制代码
另外,const保证常量的值不被修改的原理是什么呢?const实际上无法改变的只是常量在栈区的值不变,如果这个值是一个基本数据类型,那么const能够保障常量的值不变,但如果是引用类型的数据,栈区保存的其实是对应常量的地址。地址无法改变,但是对应地址的堆区内容却可以改变。
const a=[1,2,3] a.push(4) console.log(a); //[1, 2, 3, 4] 复制代码
很显然,我们通过push,直接修改了堆区的内容,间接改变了const常量的值。
如果要真正做到常量的功能,可以使用Object.freeze()
var a=[1,2,3]; Object.freeze(a) a.push(4) //Cannot add property 3, object is not extensibleat Array.push console.log(a); 复制代码
2.字符串扩展方法
(1)for of字符串遍历
使用for of可以遍历字符串,并将字符串分解为单独的字符串
var a='lang' for (item of a){ console.log(item); } // l // a // n // g 复制代码
(2) codePointAt字符串查询.
根据下标,查询对应字符串。在Es5时,就有一个charAt()方法。但charAt()显然没想到,随着Unicode编码的扩展,原先的0x0000~0xFFFF已经无法满足表示所有字符了。
var text = " "; text.charAt(0); //'' text.charAt(1); //'' 复制代码
所以es6中新增了codePointAt(),查询,codePointAt()有着更好的unicode支持,能够查询>0xFFFF的字符。
var text = " "; console.log(text.codePointAt(0)); //134071 复制代码
(3)includes()值存在判断
在es6之前使用indexof也可以进行值存在判断。includes与indexof既可以进行字符串的判断,也可以进行数组值的判断, 但是indexof在对NaN进行判断时会出现不准确。
var text = [1,NaN] console.log(text.indexOf(NaN));//-1 复制代码
另外,indexof的返回结果为-1||0,includes为true||false.
(4) repeat()字符串重复
str=repeat(n)返回的是新的字符串,并且会将str的字符串重复n次。
var a='lang' console.log(a.repeat(3));//langlanglang 复制代码
其中n会自动取整。n<=-1||n==infinaty将会报错。
(5)startwith,endwith。
startWith('str',n):返回布尔值,表示参数字符串是否在原字符串的头部。
endsWith('str',n):返回布尔值,表示参数字符串是否在原字符串的尾部。其中str表示要判断的值,n表示从目标字符串的第几个元素开始。
var str='hello world' console.log(str.startsWith('hello',0)); //true console.log(str.startsWith('world',6)); //true console.log(str.startsWith('world',0)); //false 复制代码
(6)padStart(),padEnd()
es6提供了两个字符串追加的方法String.prototype.padStart和String.prototype.padEnd,方便我们将一个新的字符串追加到某个字符串的头尾。
我们常常使用padstart来使字符串输出时保持格式。
var a='abc' var b='abcd' var c='abcd' console.log(a.padStart('10',0)); console.log(b.padStart('10',0)); console.log(c.padStart('10',0)); //0000000abc //000000abcd //00000abcde 复制代码
但有时候使用endstart显然会更好。
var a='abc' var b='abcd' var c='abcde' console.log(a.padEnd(10,'-------')); console.log(b.padEnd(10,'-------')); console.log(c.padEnd(10,'-------')); //abc------- //abcd------ //abcde----- 复制代码
也可以组合使用达到效果
var obj={ name:'wangcai', car:'BMW', wife:'fatgirl' } for(var item in obj){ console.log(item.padEnd(10,'-')+'value:'+obj[item].padStart(10,'**')); } //name------value:***wangcai //car-------value:*******BMW //wife------value:***fatgirl 复制代码
(7)**模板字符串
模板字符串的引入是es6的一大亮点,它使得输出模板变得简洁而方便。模板采用反引号(``),中间支持各种格式的输出。 包括换行,空格,变量,表达式,调用函数等。我们可以在一个模板中组合使用它们
var age=22; var name='lang' var say=()=>{ return 'hello' } var str=`myname is${name} my age is ${age} and i can say ${say()}` console.log(str); //myname islang my age is 22 and i can say hello 复制代码
在模板字符串的 ${} 中可以写任意表达式,但是同样的,对 if / else 判断、循环语句无法处理。
但在很多时候我们需要去使用if或者循环。我们可以先在外部使用逻辑处理语句,然后生成一个我们想要的模板,在用``进行转义
var age=22; var name='lang' var name2='Lang' var str='' var say=()=>{ return 'hello' } if(age>=18){str=name2} var str=`myname is${str} my age is ${age} and i can say ${say}` console.log(str); //myname isLang my age is 22 and i can say hello 复制代码
当然,我们也可以使用数组,将各个模板片段存入数组之中,最后通过Array.join('')将其拼接为最终的模板。
3.数值扩展方法
(1)二进制和八进制表示法
之前在es5时的严格模式中,,我们已经无法使用二进制和八进制。在es6中提供了两个新的二进制和八进制写法。
二进制(0bxxx||0Bxxx)八进制(0oxxx||0Oxxx)
'use strict' var a=0b11; var b=0o11 console.log(a);//3 console.log(b);//9 复制代码
(2)Number.isNaN()
isNAN可以用来检测数据是否是NAN类型,只有NAN才会返回true,其余类型皆返回false。
var x=NaN*2 console.log(Number.isNaN(x));//true console.log(NaN==NaN);//false console.log(NaN===NaN);//false 复制代码
(3)Number.parseInt(), Number.parseFloat()
这两个方法在es5中已经存在,es6将其从全局对象中提取放入了Number对象中
(4)Number.isIntger()
var a=3.00 var b=10; var c=false; var d=4.00000000000000000000000000000002 console.log(Number.isInteger(a));//true console.log(Number.isInteger(b));//true console.log(Number.isInteger(c));//false console.log(Number.isInteger(d));//true 复制代码
只有整数,以及类似3.0这样的浮点数才会被认为是整数,返回true,除此之外,js运算具有不准确性,超出精度范围的值,会默认为o,所以4.0000000000000000000000002会被看做是4. (5)Math.trunc()
取整函数,会对值进行取整,会对传入的值先进行Number()处理,正数将会进行Math.floor(),若为负数则进行Math.ceil();
console.log(Math.trunc(4.1));//4 console.log(Math.trunc(4.9));//4 console.log(Math.trunc(-1.2));//-1 console.log(Math.trunc(-1.9));//-1 console.log(Math.trunc(true));//1 console.log(Math.trunc('lang'));//NaN 复制代码
3.函数扩展
(1)函数指定默认值 可以为函数传入的参数指定默认值,函数内部可以覆盖使用。
function say(x,y=5){ console.log(x,y); //1,5 y=10; console.log(x,y); //1,10 } say(1) 复制代码
需要注意以下两点
.使用参数默认值时,函数不能有同名参数。
// 不报错 function foo(x, x, y) { // ... } // 报错 function foo(x, x, y = 1) { // ... } // SyntaxError: Duplicate parameter name not allowed in this context 复制代码
.不可使用let,const重复声明
function say(x,y=5){ let y=10; console.log(x,y); //SyntaxError: Identifier 'y' has already been declared } say(1) 复制代码
(2)rest 参数 在函数形参中使用...扩展运算符,可以将不定形参传入rest数组中。
function say(...res) { for (var item of res) { console.log(item); } } say(1, 2, 3) //1 //2 //3 复制代码
(3)箭头函数(重点)
基本使用方法
var f=(x,y)=>{ return x+y} console.log(f(1,2)); //3 复制代码
假如(x,y)只有一个参数,我们可以省略(),同样返回语句中,若只有一条语句,也可以省略。
var f=x=>x+10 console.log(f(1)); //11 复制代码
使用注意点 箭头函数有几个使用注意点。
(1)函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
(2)不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
(3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
(4)不可以使用yield命令,因此箭头函数不能用作 Generator 函数。
对于需要注意的第一点,我们经常会因为维护回调函数的this而烦恼,而在箭头函数中则不存在这个问题,箭头函数内部的this是固定的,不会因为函数调用而改变。
在ES5,我们通常采用外部保存this的方法,来维护this、
// ES6 function foo() { setTimeout(() => { console.log('id:', this.id); }, 100); } // ES5 function foo() { var that= this; setTimeout(function () { console.log('id:', that.id); }, 100); } 复制代码
argumet在箭头函数中不存在,那么使用argument将会使用外部函数的argument
function foo() { setTimeout(() => { console.log('args:', arguments); }, 100); } foo(2, 4, 6, 8) // args: [2, 4, 6, 8] 复制代码
(4)尾调用
在函数的最后一步调用一个函数,这叫做尾调用。解释很简单,但却很容易混淆。 .最后一步?什么是最后一步,在函数中,return就是最后一步,没有return的都不是尾调用。
function g(){} function f(){ g() //这不是尾调用 } f() 复制代码
.即使return后面还有表达式,但这些函数不起作用,那么它依旧是尾调用。
function g(){} function f(){ return g() //这是尾调用 console.log('121'); } f() 复制代码
.返回的必须是函数,不能是表达式。下面额例子中,返回了g()+1; g()+1;不是一个函数,而是一个表达式。
function g(){} function f(){ return g()+1 //这不是尾调用 } f() 复制代码
(5)尾递归
尾递归是尾调用的一种特殊情况,但尾递归是在最后一步调用自身。
我们在使用递归时,必须给一个终止条件,不然就会产生死循环
var a=0; function f(){ a++ return f() //这显然是一个死循环 } f() 复制代码
我们常常使用递归等方法来求阶乘等,但递归很容易发生栈溢出的情况。
非尾递归的 Fibonacci 数列实现如下。
function Fibonacci (n) { if ( n <= 1 ) {return 1}; return Fibonacci(n - 1) + Fibonacci(n - 2); } Fibonacci(10) // 89 Fibonacci(100) // 堆栈溢出 Fibonacci(500) // 堆栈溢出 复制代码
尾递归优化过的 Fibonacci 数列实现如下。
function Fibonacci2 (n , ac1 = 1 , ac2 = 1) { if( n <= 1 ) {return ac2}; return Fibonacci2 (n - 1, ac2, ac1 + ac2); } Fibonacci2(100) // 573147844013817200000 Fibonacci2(1000) // 7.0330367711422765e+208 Fibonacci2(10000) // Infinity 复制代码
4.数组扩展
(1)扩展运算符(...)
使用扩展运算符可以 能够直接深拷贝一个数组。修改一个数组内的引用值,不会改变另一个值
var arr=[1,2,3,[4,5],6] var a=[...arr]; console.log(a);//[1, 2, 3, Array(2), 6] 复制代码
扩展运算符可以用于数组拼接
var arr=[1,2,3] var arr2=[4,5,6] var str='12121' var a=[1,...arr,2,...arr2]; 复制代码
另外...arr返回的并不是一个数组,而是各个数组的值。只有[...arr]才是一个数组,所以...arr。可以用来对方法进行传值
var arr=[1,2,3] function f(x,y,z){ return x+y+z } console.log(f(...arr)); //6 复制代码
(2)Array.from()
Array.from()可以将某些伪数组转换成真正的数组结果,什么是伪数组呢,我们在实际开发中,有两种常见的伪数组,arguments和Dom中获取的元素集合。
<body> <p></p> <p></p> <p></p> <p></p> <p></p> </body> <script> var arr = document.getElementsByTagName('p') var arr2 = Array.from(arr) console.log(Array.isArray(arr2));//true 复制代码
同样能够将伪数组转换数组的还有两种方法 .之前提到的扩展运算符
var arr = document.getElementsByTagName('p') var arr2 = [...arr] console.log(Array.isArray(arr2));//true 复制代码
.使用call,apply方法。
var arr = document.getElementsByTagName('p') var newarr=Array.prototype.slice.call(arr,0) var newarr=Array.prototype.slice.apply(arr,[0]) console.log(Array.isArray(newarr));//Var 新数组 = 旧数组.slice(起点下标,终点下标)返回值:数组,是旧数组中的一个部分。 console.log(newarr); 复制代码
(3)Array.of()
也是用于将一组值,转换为数组。 Array.of并不是用于将转换伪数组的,它的作用是为了弥补Array构造器的不足,之前我们在想要构建一个长度为一的数组,且值为number类型是无法用Array构建的
var arr=new Array(3) console.log(arr);//[empty*3] 复制代码
很显然,我们想要构建一个[3],可以使用Array.of()
var arr=Array.of(3) console.log(arr); //[3] 复制代码
(4)find和findIndex 查询第一个符合条件值/下标
find:用于找出第一个符合条件的数组元素。找不返回 undefined 。
findIndex:返回第一个符合条件的数组元素的索引。找不到返回-1;
var arr=[1,2,3,4,5] var newarr1=arr.find(function(item,index){return item>2}) var newarr2=arr.findIndex(function(item,index){return item>2}) console.log(newarr1); //3 console.log(newarr2); //2 复制代码
基本语法如上:find和findindex内部是一个回调函数,需要返回一个查询条件,find则会执行这个返回条件,查询第一个满足条件的值。findindex则会返回下标。 我们可以直接用箭头函数进行简写
var arr=[1,2,3,4,5] var newarr1=arr.find(item=>item>2) var newarr2=arr.findIndex(item=>item>2) console.log(newarr1); //3 console.log(newarr2); //2 复制代码
(5)fill 填充初始化数组
作用:给数组填充指定值。fill 方法用于空数组的初始化非常方便。已有数据会被覆盖。 fill 方法还可以接受第二个和第三个参数,用于指定填充的起始位置和结束位置
var arr=[1,2,3,4,5] arr.fill('*',1,3) console.log(arr);//[1, "*", "*", 4, 5] 复制代码
5.对象扩展
(1).属性的简写
属性名是可以简写的,但是有前提条件:属性的值是一个变量 变量名称和键名是一致的。
var name ='lang' var age=22; var obj={ name:name, age:age } 复制代码
像这样的对象,我们就可以进行简写
var name ='lang' var age=22; var obj={ name, age, } 复制代码
前提是属性名和属性值必须一致。
(2)方法的简写
var obj={ say:function(){} } 复制代码
简写为
var obj={ say(){} } 复制代码
(3)精细化设置对象的属性
属性的四个特征:
1.configurable: 是否可以删除。 默认为true 可以删除:
2.writable: 是否可以修改。 默认为ture, 可以修改:
3.enumerable: 是否可以枚举。可以使用 for in 默认为ture, 可以枚举:
4.value: 值。 默认值为undefined
格式1:Object.defineProperty(对象名,“属性名”,{上面的四个特征});
var obj={ name:'lang', age:22 } Object.defineProperty('obj','name',{ configurable:false, writable:false, enumerable:false }) 复制代码
格式2:Object.defineProperties(对象名,{属性名{四个特征}},{属性名{四个特征}}});
var obj = { name: 'lang', age: 22 } Object.defineProperties(obj, { name: { configurable: false, writable: false, enumerable: false }, age: { configurable: false, writable: false, enumerable: false } }) 复制代码
(4)获取精细化设置对象的属性
Obect.getOwnProertyDescriptor(对象,属性名) 可以获取对象的精细化属性里面的值。
var obj={ name:'lang', age:22 } Object.defineProperty(obj,'name',{ configurable:false, writable:false, enumerable:false }) console.log(Object.getOwnPropertyDescriptor(obj,'name')); 复制代码
(5)Object.keys()
使用Object.getOwnPropertyNames()和Object.keys()都可以得到一个对象的属性名,属性名是放在一个数组中的。
var obj={ name:'lang', age:22 } console.log(Object.keys(obj)); //["name", "age"] 复制代码
那么我们目前有三种遍历对象的方法了 对于对象的遍历目前有三种方式:
1. for in 2.Object.keys() 3.Object.getOwnPropertyNames() 复制代码
for in : 会输出自身以及原型链上可枚举的属性。
Object.keys() : 用来获取对象自身可枚举的属性键
Object.getOwnPropertyNames() : 用来获取对象自身的全部属性名
(6)Object.values();
获取对象的值,放入数组中。
var obj={ name:'lang', age:22 } console.log(Object.values(obj)); //["lang", 22] 复制代码
6.解构赋值
解构赋值语法是一个 Javascript 表达式,这使得可以将值从数组或属性从对象提取到不同的变量中。
对象字面量和数组字面量提供了一种简单的定义一个特定的数据组的方法。
let x = [1, 2, 3, 4, 5]; 复制代码
解构赋值使用了相同的语法,不同的是在表达式左边定义了要从原变量中取出什么变量。
var x = [1, 2, 3, 4, 5]; var [y, z] = x; console.log(y); // 1 console.log(z); // 2 复制代码
(1)数组的解构
变量声明并赋值时的解构
var foo = ["one", "two", "three"]; var [one, two, three] = foo; console.log(one); // "one" console.log(two); // "two" console.log(three); // "three" Link to section变量先声明后赋值时的解构 复制代码
通过解构分离变量的声明,可以为一个变量赋值。
var a, b; [a, b] = [1, 2]; console.log(a); // 1 console.log(b); // 2 Link to section默认值 复制代码
为了防止从数组中取出一个值为undefined的对象,可以为这个对象设置默认值。
var a, b; [a=5, b=7] = [1]; console.log(a); // 1 console.log(b); // 7 Link to section交换变量 复制代码
在一个解构表达式中可以交换两个变量的值。
没有解构赋值的情况下,交换两个变量需要一个临时变量(或者用低级语言中的XOR-swap技巧)。
var a = 1; var b = 3; [a, b] = [b, a]; console.log(a); // 3 console.log(b); // 1 Link to section解析一个从函数返回的数组 复制代码
从一个函数返回一个数组是十分常见的情况.。解构使得处理返回值为数组时更加方便。
在下面例子中,[1, 2] 作为函数的 f() 的输出值,可以使用解构用一句话完成解析。
function f() { return [1, 2]; } var a, b; [a, b] = f(); console.log(a); // 1 console.log(b); // 2 复制代码
(2)对象的解构
基本赋值
var o = {p: 42, q: true}; var {p, q} = o; console.log(p); // 42 console.log(q); // true 复制代码
给新的变量名赋值
可以从一个对象中提取变量并赋值给和对象属性名不同的新的变量名。
var o = {p: 42, q: true}; var {p: foo, q: bar} = o; console.log(foo); // 42 console.log(bar); // true 复制代码
默认值
变量可以先赋予默认值。当要提取的对象没有对应的属性,变量就被赋予默认值。
var {a = 10, b = 5} = {a: 3}; console.log(a); // 3 console.log(b); // 5 复制代码
混合解构(嵌套对象和数组) 解构嵌套对象和数组
var metadata = { title: "Scratchpad", translations: [ { locale: "de", localization_tags: [ ], last_edit: "2014-04-14T08:43:37", url: "/de/docs/Tools/Scratchpad", title: "JavaScript-Umgebung" } ], url: "/en-US/docs/Tools/Scratchpad" }; var { title: englishTitle, translations: [{ title: localeTitle }] } = metadata; console.log(englishTitle); // "Scratchpad" console.log(localeTitle); // "JavaScript-Umgebung" 复制代码
7.class类
之前在es5中,我们如何去实现一个类的功能呢?我们通常采用构造器的方法去实现,然而,使用构造器去模仿类的实现并不方便,不仅需要经常维护this,而且在继承的时候更是不仅需要使用call拷贝父类的基本数值,还需要继承父类的原型来继承方法。我们简单来看看代码就知道了
function Parent(name) { this.name = name; } Parent.prototype.sayName = function () { console.log('parent name:', this.name); } function Child(name, parentName) { Parent.call(this, parentName); this.name = name; } function inheritPrototype(Parent, Child) { Child.prototype = Object.create(Parent.prototype); //修改 Child.prototype.constructor = Child; } inheritPrototype(Parent, Child); Child.prototype.sayName = function () { console.log('child name:', this.name); } var parent = new Parent('father'); parent.sayName(); // parent name: father var child = new Child('son', 'father'); child.sayName(); // child name: son 复制代码
在es6中,我们可以直接使用class来定义,如果你有 Java 基础的话,那么理解起来将非常简单。 基本上,ES6 的class可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。
//定义类 class Point { constructor(x, y) { this.x = x; this.y = y; } toString() { return '(' + this.x + ', ' + this.y + ')'; } } 复制代码
我们使用typeof去检测class的数据类型,会发现class类的本质就是一个方法,也就是构造器。我们不仅仅可以使用new方法来新建一个类,我们也可以使用prototype来访问类的原型。
(1)constructor
可以看到里面有一个constructor方法,这就是构造方法,而this关键字则代表实例对象。这里面通常保存着类基本数据类型
(2)定义类方法
我们可以直接在类中添加自己的方法,前面不需要加上function这个关键字,另外,为了使类更加的符合大众的写法,去掉了逗号分隔,我们不需要在方法之间使用逗号进行分隔。
(3)类的继承
es6还给类提供了一个extends的继承方法。使用方法与java如出一辙。
class NBAPlayer2 { constructor(name, height) { this.name = name; this.height = height; } say() { console.log(`name is${this.name} height is${this.height}`); } } class MVP2 extends NBAPlayer { constructor(name, height, year) { super(name, height) this.year = year } say() { console.log(`name is${this.name} height is${this.height} mvpyear is${this.year}`); } } var n1 = new MVP2('老库里', '201', '2016') var m1 = new NBAPlayer2('老库里', '201') n1.say() m1.say() 复制代码
注意:使用 extends 关键字来实现继承在子类中的构造器 constructor 中,必须要显式调用父类的 super 方法,如果不调用,则 this 不可用
我们如果使用原型去查看父子类,就会发现,他们其实是通过原型链来进行继承的。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
从零开始学微信公众号运营推广
叶龙 / 清华大学出版社 / 2017-6-1 / 39.80
本书是丛书的第2本,具体内容如下。 第1章 运营者入门——选择、注册和认证 第2章 变现和赚钱——如何从0到100万 第3章 决定打开率——标题的取名和优化 第4章 决定美观度——图片的选取和优化 第5章 决定停留率——正文的编辑和优化 第6章 决定欣赏率——版式的编辑和优化 第7章 数据的分析——用户内容的精准营销 书中从微信运营入门开始,以商业变......一起来看看 《从零开始学微信公众号运营推广》 这本书的介绍吧!