系统化学习前端之JavaScript(ES6)
前言
ES6 同样是 ECMAScript 的拓展,发布于 2015年,目前多数浏览器对于部分 ES6 更新内容不支持,通常需要借助 bable
编译器编译成 ES5
或者 ECMAScript
早期版本语法去执行。
ES6 的学习推荐阮一峰老师的 ES6 教程。
ES6
ES6 是 ECMAScript 最近一次较大版本的更新,更新内容主要是一些优化,包括开发效率的优化,以及之前版本问题的优化等。
let 和 const
-
let
let
关键字用于定义变量,优化掉了var
关键字。let
关键字定义变量的特点:-
在
{}
内部定义变量,会生成块级作用域。{ var a = 1 let b = 1 } console.log(a) // 1 console.log(b) // ReferenceError
注意:块级作用域内重复声明全局变量,则该变量绑定块级作用域。
-
抛出变量提升引起的问题。
console.log(a) // undefined var a = 1 console.log(b) // ReferenceError let b = 1
-
抛出重复声明的问题。
// 重复声明变量 function fun1() { var a = 1 var a = 2 console.log(a) // 2 } fun1() function fun2() { let a = 1 var a = 2 console.log(a) // SyntaxError:already been declared } fun2() // 重复声明参数 function fun3(x) { var x = 1 console.log(x) // 1 } fun3() function fun4(x) { let x = 1 console.log(x) // SyntaxError:already been declared } fun4()
-
-
const
const
关键字用于定义常量,常量一旦声明,必须立即赋值。const
定义常量的特点:-
在
{}
内部定义常量,会生成块级作用域。{ const A = 1 } console.log(A) // ReferenceError
-
定义常量无法修改。
const A = 1 A = 2 // TypeError: Assignment to constant variable
注意:const 也具备 let 相同的特点。
-
解构赋值
-
数组解构
数组解构分为两种:常规解构和默认值解构
-
常规解构
let [a, b, c] = [1, 2, 3] console.log(a, b, c) // 1, 2, 3 let [x, , y, z] = [1, 2, 3] console.log(x, y, z) // 1, 3, undefined let [i] = [1, 2, 3] console.log(i) // 1 let [u, ...v] = [1, 2, 3] console.log(u, v) // 1, [2, 3]
-
默认值解构
let [a = 4, b, c, d = 3] = [1, 2] console.log(a, b, c, d) // 1, 2, undefined, 3
注意:解构有值,则默认值无效,解构无值,则默认值有效。
-
-
对象解构
对象解构分为三种:常规解构,换名解构,默认值解构。
-
常规解构
let obj = { name: 'jim', age: 20 } let { name, age, gender } = obj console.log(name, age, gender) // jim, 20, undefined
-
换名解构
let obj = { name: 'jim', age: 20 } let { name: call } = obj console.log(call) // jim
注意:换名解构相当于定义一个变量,通过解构出来的值进行赋值操作。
-
默认值解构
let obj = { name: 'jim', age: 20 } let { name: call = 'jack', gender: sex = 'male' } = obj console.log(call, sex) // jim, male
注意:解构有值,则默认值无效,解构无值,则默认值有效。
-
-
字符串解构
字符串解构分为两种:字符解构和长度解构
-
字符解构
let [a, b, , c, d] = 'hello' console.log(a, b, c, d) // h, e, l, o
-
长度解构
let { length: len } = 'hello' console.log(len) // 5
-
-
函数参数解构
函数参数解构分为两种:数组参数解构和对象参数解构
-
数组参数解构
function sum(x, y) { return x + y } sum(...[1,2]) // 3
-
对象参数解构
function sum({ x = 0, y = 1 } = {}) { return x + y } sum() // 1 sum({ x: 1 }) // 2 sum({ x: 2, y: 2 }) // 4
-
模板字符串
模板字符串是一种支持内置表达式的字符串,简化字符串拼接操作。
let price = 12.5
let count = 5
console.log(`
单价:¥${price.toFixed(2)}
数量:${count}
=====================
总价:¥${(price * count).toFixed(2)}
`)
模板字符串的特点:
-
模板字符串使用 `` 反引号包裹。
-
模板字符串支持使用 js 表达式,使用
${}
插值,插值内为 js 环境。 -
模板字符串支持换行。
BigInt
JavaScript 在数值表示存在两个问题:
-
大于或等于 2^53 的数值无法保证精度。
Math.pow(2, 53) === Math.pow(2, 53) + 1 // true
-
大于或等于 2^1024 的数值无法表示,会返回
Infinity
。Math.pow(2, 1024) // Infinity
注意:可以使用
isInfinity()
判断是否为Infinity
。
BigInt 类型用于解决上述问题,没有位数限制,任何位数的整数都可以精确表示。
注意:
a. BigInt 类型与普通整数数值相等,类型不同。如
2n == 2
返回 true,2n === 2
返回false。b. BigInt 类型不能使用
+
表示正数,会编译成隐式转换,而 BigInt 类型无法转换 Number 类型。c. BigInt 类型可以正常进行相关运算。
定义 BigInt 类型的方式:
-
字面量定义
let x = 2n // 整数后缀 n,表示 bigint 类型 typeof x // bigint
-
转换函数定义
let x = BigInt(2) typeof x // bigint
注意:
BigInt()
只能转换整型、整型字符串以及 boolean 值,其他无法转换,抛出错误。
运算符拓展
-
指数运算符
let a = 2 ** 3 // 2^3 a **= 2 // 64
-
链判断运算符
链判断运算符用于判断对象的属性和方法是否存在。
let obj = { a: '1', b: { c: 2 }, run() { console.log('running') } } // ES6 之前 let x = obj && obj.b && obj.b.c || undefined // ES6 之后 let x = obj?.b?.c // 属性存在则赋属性值,不存在则赋值 undefined obj.run?.() // 方法存在则执行方法,不存在则不执行
-
Null 判断运算符
变量赋值默认值的方法
let x = y || 300 // y 为变量
上例中当 y 存在则赋值给 x,当 y 不存在则赋值 x 为 300。y 不存在具体含义:y 为 undefined 或者 null 时,当上例中 y 为 0 或 false 也满足不存在的条件。
// ES6 let x = y ?? 300
只有当 y 为 undefined 或者 null 时,才会给 x 赋值 300。
-
逻辑赋值运算符
逻辑赋值运算符有:
&&=
,||=
,??=
。x &&= y
等同于x = x && y
。
函数拓展
-
参数拓展
-
函数参数默认值
// ES6 之前 function sum(x, y) { y = y ?? 0 return x + y } // ES6 function sum(x, y = 0) { return x + y }
-
rest 参数
// ES6 之前 function sum() { console.log(arguments) // Arguments[1,2] 类数组对象 } // ES6 function sum(...params) { console.log(params) // [1,2] 数组 } sum(1,2)
注意:
...params
参数之后不能再出现任何形参,否则报错。之前出现形参,则为形参解构实参。
-
-
箭头函数
箭头函数是对匿名函数的简写方式。
let log = function (info) { console.log(info) } let log = info => { console.log(info) } // 函数只有一个参数,则可以不使用 `()` 包括。 let sum = function (x, y) { return x + y } let sum = (x, y) => x + y // 函数只有返回值一句,则可以省略 `{}`。 let co = function (a, b) { return { name: a, age: b } } let co = (a, b) => ({ name: a, age: b }) // 返回值是一个对象,则需要使用 `()` 包括。
注意:
a. 箭头函数内部的
this
始终指向其上层作用域的this
,不可改变,即call
,apply
,bind
无效。let obj = { name: 'json', age: 20 } function outer() { let inner = () => { console.log('inner',this) } inner() console.log('outer', this) } outer() // inner window outer window outer.call(obj) // inner {name: 'json', age: 20}, outer {name: 'json', age: 20}
b. 箭头函数内部不可以使用
arguments
获取参数,会抛出错误。c. 不能使用
new
和yield
命令,即不能作为构造函数和 Generator 函数。
数组拓展
-
扩展运算符
扩展运算符指
...
运算符,作用:-
数组打散
let arr = [1, 2, 3] console.log(...arr) // 1 2 3
-
数组解构
let arr = [1, 2, 3] let [ a, ...b ] = arr console.log(a, b) // 1, [2, 3]
-
对象解构
let obj = { a: 1, b: 2, c: 3 } let { a, ...rest } = obj console.log(a, rest) // 1, { b: 2, c: 3 }
-
-
Array.from()
可将类数组对象和可迭代对象(如 Set 和 Map)转换为真正的数组。
let arr = Array.from(arr-like)
-
Array.of()
将一组参数转换为数组。
let arr = Array.of(1,2,3) // [1, 2, 3]
对象拓展
-
属性和方法简化
let name = 'jim' let age = 21 // ES6 之前 let obj = { name: name, age: age, run: function() { return `${this.name} is running` } } // ES6 let obj = { name, // 同名属性可以简写 age, run() { return `${this.name} is running` // 方法可以简写 } }
-
遍历对象属性
对象属性中的
enumerable
特性设置为 true,表示当前对象属性可以遍历。遍历对象属性的方法有:-
for...in
let obj = { name: 'jason', age: 18 } for(let key in obj) { console.log(key, obj[key]) // name jason, age 18 }
-
Object.keys(obj)
let obj = { name: 'jason', age: 18 } let res = Object.keys(obj) console.log(res) // ['name', 'age']
-
Object.getOwnPropertyNames(obj)
let obj = { name: 'jason', age: 18 } let res = Object.getOwnPropertyNames(obj) console.log(res) // ['name', 'age']
-
Reflect.ownKeys(obj)
let obj = { name: 'jason', age: 18 } let res = Reflect.ownKeys(obj) console.log(res) // ['name', 'age']
-
-
新增对象方法
-
Object.is()
0 === -0 // true NaN === NaN // false Object.is(0, -0) // false Object.is(NaN, NaN) // true
-
Object.assign()
Object.assign()
接收多个参数对象,第一个参数是目标对象,后面参数都是源对象,该方法是将源对象的所有属性复制到目标对象,返回一个合并后的对象。如果存在同名属性,则后面的属性会覆盖前面的属性。let obj1 = { a: 1, b: 2, c: 3 } let obj2 = { d: 4, e: 5, a: 6 } Object.assign({}, obj1, obj2) // {a: 6, b: 2, c: 3, d: 4, e: 5}
目标对象参数不为对象,则报错,源对象参数不为对象,会进行对象转化,转化后处理,只有 String 和 Array 类型可以转换。
Object.assign({}, 1.5) // {} Object.assign({}, true) // {} Object.assign({}, null) // {} Object.assign({}, undefined) // {} Object.assign({}, 'hello') // {0: 'h', 1: 'e', 2: 'l', 3: 'l', 4: 'o'} Object.assign({}, [1,2,3]) // {0: 1, 1: 2, 2: 3}
Object.assign()
拷贝对象属于浅拷贝,参数对象的属性为引用类型,实际拷贝的是属性的引用。let obj = { a: '1', b: { c: 2 } } let cobj = Object.assign({}, obj) obj.b.c = 20 console.log(cobj) // { a: '1', b: { c: 20 } }
实现深拷贝的两种方式:
// 方式一 let cobj = JSON.parse(JSON.stringify(obj))
注意:方式一中,对象属性值如果是 undefined,Symbol 类型,Function 类型会被过滤,因此只适合符合 JSON 风格的对象。
// 方式二 function deepCopy(arg) { if(typeof arg === "object") { var result = arg.constructor === Array ? [] : {}; for(let i in arg) { result[i] = typeof arg[i] === 'object' ? deepCopy(arg[i]) : arg[i]; } } else { var result = arg; } return result }
-
Object.hasOwn()
Object.hasOwn(obj, 'attr')
接收两个参数,分别为对象和属性,用于判断attr
是否为obj
自有属性。同obj.hasOwnProperty()
一致。
-
正则拓展
正则拓展是指拓展 JavaScript 的正则语法,未增加新的 API 方法。
-
先行断言
/x(?=y)/
表示匹配在y
之前的x
。 -
先行否定断言
/x(?!y)/
表示匹配在不是y
之前的x
。 -
后行断言
/(?<=y)x/
表示匹配在y
之后的x
。 -
后行否定断言
/(?<!y)x/
表四匹配不是y
之后的x
。/(?<=(o)d\1)r/.exec('hodor') // null /(?<=\1d(o))r/.exec('hodor') // ["r", "o"]
注意:后行断言的分组引用需要将分组引用前置。
-
分组命名
通过
(?<name>)
可以为分组命名,使用reg.exec()
返回的类数组对象可以获取对应分组匹配结果。let reg = /(?<year>\d{4})-(?<month>\d{1,2})-(?<day>\d{1,2})/ let res = reg.exec('2023-3-31') console.log(res.groups.year) // 2023 console.log(res.groups.month) // 3 console.log(res.groups.day) // 31
分组命名以后,可以获取分组名字,则可以通过解构获取对应分组匹配结果。
let reg = /(?<year>\d{4})-(?<month>\d{1,2})-(?<day>\d{1,2})/ let { groups: { year, month, day } } = reg.exec('2023-3-31') console.log(year) // 2023 console.log(month) // 3 console.log(day) // 31
注意:分组命名,可以通过
\k<name>
引用分组。
Set 和 Map
-
Set
Set 是类似于数组的数据结构,其成员都是唯一的。
创建 Set 类型数据
// 方式一:实例化传入数组参数 let set = new Set([1,2,3,4,3]) // Set(4) {1, 2, 3, 4} // 方式二:实例方法添加成员 let set = new Set() // Set(4) {1, 2, 3, 4} set.add(1) set.add(2) set.add(3) set.add(4) set.add(3)
Set 类型属性
-
set.size
Set 类型数据长度。
let set = new Set([1,2,3,4,3]) console.log(set.size) // 4
Set 类型方法
-
set.add
接收一个参数,向 set 中添加成员,成员可以为任意类型,返回添加后的 Set ,如
set.add(1)
。 -
set.delete
接收一个参数,从 set 中删除成员,若删除存在成员则返回 true,删除不存在成员则返回 false,如
set.delete(3)
。 -
set.has
接收一个参数,判断参数是否为 set 成员,是则返回 true,否则返回 false,如
set.has(2)
。 -
set.clear
清空 set 中所有成员。
-
set.forEach
遍历 set 中的成员,如
set.forEach((item, index) => { ... })
, 其中item
和index
是相同的,均为 set 中的成员。
注意: Set 常用简单数组去重, 如
[...new Set(arr)]
。 -
-
WeakSet
WeakSet 是特殊的 Set 数据类型,其成员须是对象或者类数组对象,成员是唯一的,WeakSet 不具有长度属性。
创建 WeakSet 类型数据
// 方式一: 实例化传入数组参数 let weakSet = new WeakSet([{}, [1,2,3]]) // 方式二:实例方法添加成员 let weakSet = new WeakSet() weakSet.add({}) weakSet.add([1,2,3])
WeakSet 方法
-
weakSet.add
接收一个参数,参数必须为引用类型属性,返回添加后的 weakSet ,如
weakSet.add({})
。 -
weakSet.delete
接收一个参数,删除 weakSet 中成员。weakSet 均为引用类型数据,删除只是删除对应引用,无法真正删除,一直返回 false。
-
weakSet.has
接收一个参数,判断参数是否在 weakSet 中,一直返回 false,因为
{} !== {}
。
-
-
Map
Map 是类似于对象的数据结构,其键值对中的键可以是任意类型数据,区别对象的键只能是字符串。
创建 Map 类型数据
// 方式一:接收数组作为参数,数组中元素须是键值对组成的数组 let map = new Map([['name', '张三'], ['age', 18]]) // {'name' => '张三', 'age' => 18} let map = new Map() // Map(3) {'name' => '张三', 'age' => 18, {…} => 'object'} let obj = { a: 1 } map.set('name', '张三') map.set('age', 18) map.set(obj, 'object')
Map 类型属性
-
map.size
map 的长度。
Map 类型方法
-
map.set
接收两个参数,第一个参数为键,第二个参数为值,为 map 添加成员,如
map.set('age', 18)
-
map.get
接收一个参数,参数为键,用于查找当前 map 对应键的值,未找到返回 undefined,如
map.get('name')
。 -
map.has
接收一个参数,参数为键,判断参数是否为 map 中的键,是则返回 true,否则返回 false,如
map.has(obj)
返回 true。 -
map.delete
接收一个参数,参数为键,删除当前 map 中的键值对,如
map.delete(obj)
。 -
map.clear
清空当前 map 中所有成员。
-
map.forEach
遍历 map 中的成员,如
map.forEach((value, key, map) => { ... })
,其中回调函数的第一个参数为value,第二参数为 key。
-
-
WeakMap
WeakMap 是特殊的 Map 类型数据,其键必须为对象,不能使用 null,WeakMap 不具有长度属性。
创建 WeakMap 数据
let obj = {} let weakMap = new WeakMap() weakMap.set(obj, 1)
WeakMap 类型方法
-
weakMap.set
接收两个参数,第一个参数为键,第二个参数为值,为 map 添加成员,其键必须为对象,如
weakMap.set(obj, 1)
。 -
weakMap.get
接收一个参数,参数为键,用于查找当前 map 对应键的值,未找到返回 undefined,如
weakMap.get(obj)
。 -
weakMap.has
接收一个参数,参数为键,判断参数是否为 map 中的键,是则返回 true,否则返回 false,如
weakMap.has(obj)
返回 true。
-
Proxy
Proxy 是指对象的代理,对象属性的保护,在 ES5 中可以通过 Object.defineProperty
设置属性特性进行保护,ES6 中通过代理对象 Proxy
进行保护。PS:说是保护,更多是拦截操作,通过重写方法的方式进行保护或者禁止等其他操作。
-
创建代理对象
通过构造函数法可以创建代理对象
let proxy = new Proxy(target, handler) // target 为被代理对象,即目标对象,handler 为拦截方法对象,proxy 为代理对象。
-
拦截方法对象
拦截方法对象是指由多个 proxy 实例方法组成的对象,通过重写方法可以实现对拦截对象的保护或者其他操作。如
{ get() { ... }, set() { ... }, ... }
。proxy 实例方法有:
-
get
该方法用于拦截指定属性的读取操作,接收三个参数,分别对应目标对象,指定属性以及代理对象。
let obj = { name: 'jason', age: 18 } let proxy = new Proxy(obj, { get(target, key, proxy) { if(age in target) { return target[key] + 1 } else { throw new ReferenceError('属性不存在!') } } }) console.log(proxy.age) // 19 console.log(proxy.gender) // ReferenceError
-
set
该方法用于拦截指定属性的写入操作,接受四个参数,分别对应目标对象,指定属性,写入值以及代理对象。
let obj = { name: 'jason', age: 18 } let proxy = new Proxy(obj, { set(target, key, value, proxy) { target[key] = value + 1 } }) proxy.age = 20 console.log(proxy.age) // 21
注意:
get
和set
方法中不能使用proxy[key]
的方式读取或写入属性,回发生循环引用问题,导致栈溢出。
proxy 还有许多其他实例方法,如
apply
,has
,construct
,defineProperty
,deleteProperty
,getOwnPropertyDescriptor
,getPrototypeOf
,setPrototypeOf
,ownKeys
,isExtensible
,preventExtensions
,isSealed
,seal
,isFrozen
,freeze
等。 -
注意:proxy 代理 target,target 对象的 this 是指向 proxy 对象的,可以通过
bind
的更改。外次,实例方法的this
指向 拦截方法对象,即 handler 对象。
Reflect
Reflect 对象是为了抽离部分 Object 静态方法的 API 封装,部分 Object 静态方法应用不局限于 Object,这类方法可以提取在新对象中使用。
-
get
var obj = { foo: 1, bar: 2, get baz() { return this.foo + this.bar; }, } Reflect.get(obj, 'foo') // 1 Reflect.get(obj, 'bar') // 2 Reflect.get(obj, 'baz') // 3
-
set
var obj = { foo: 1, set bar(value) { return this.foo = value; }, } obj.foo // 1 Reflect.set(obj, 'foo', 2); obj.foo // 2
-
apply
function fun(x, y) { console.log(this, x, y) } fun(1,2) // window 1 2 let obj = { name: 'jim' } Reflect.apply(fun, obj, [1,2]) // obj 1 2
-
construct
function Greeting(name) { this.name = name; } // new 的写法 const instance = new Greeting('张三'); // Reflect.construct 的写法 const instance = Reflect.construct(Greeting, ['张三']);
-
defineProperty
let obj = { foo: 'bar', } Reflect.defineProperty(obj, 'foo', {...})
注意:
Reflect.defineProperty(obj, 'foo', {...})
等效于Object.defineProperty(obj, 'foo', {...})
。 -
deleteProperty
const obj = { foo: 'bar' } Reflect.deleteProperty(obj, 'foo')
注意:
Reflect.deleteProperty(obj, 'foo')
等效于delete myObj.foo
。 -
getOwnPropertyDescriptor
var obj = {}; Object.defineProperty(obj, 'hidden', { value: true, enumerable: false, }); // 旧写法 var theDescriptor = Object.getOwnPropertyDescriptor(obj, 'hidden'); // 新写法 var theDescriptor = Reflect.getOwnPropertyDescriptor(obj, 'hidden');
-
getPrototypeOf
const obj = new Animal(); // 旧写法 Object.getPrototypeOf(obj) === Animal.prototype; // 新写法 Reflect.getPrototypeOf(obj) === Animal.prototype;
-
setPrototypeOf
const obj = {}; // 旧写法 Object.setPrototypeOf(obj, Array.prototype); // 新写法 Reflect.setPrototypeOf(obj, Array.prototype);
-
has
var obj = { foo: 1, } Reflect.has(obj, 'foo') // true
注意:
Reflect.has(obj, 'foo')
等效于'foo' in obj
。 -
ownKeys
var obj = { foo: 1, bar: 2, [Symbol.for('baz')]: 3, [Symbol.for('bing')]: 4, }; // 旧写法 Object.getOwnPropertyNames(obj) // ['foo', 'bar'] Object.getOwnPropertySymbols(obj) //[Symbol(baz), Symbol(bing)] // 新写法 Reflect.ownKeys(obj) // ['foo', 'bar', Symbol(baz), Symbol(bing)]
-
isExtensible
const obj = {}; // 旧写法 Object.isExtensible(obj) // true // 新写法 Reflect.isExtensible(obj) // true
-
preventExtensions
var obj = {}; // 旧写法 Object.preventExtensions(obj) // Object {} // 新写法 Reflect.preventExtensions(obj) // true
for..in 和 for...of
-
for...in
for (let key in iteratableObj) { // key 为 iteratableObj 可迭代对象的键,字符串表示。 }
注意:
for...in
可迭代对象包括:Array,Map,Set,String,对象以及类数组对象。 -
for...of
for (let value in iteratableObj) { // value 为 iteratableObj 可迭代对象的值。 }
注意:
for...of
可迭代对象包括:Array,Map,Set,String 以及类数组对象,不包含对象。
Class
Class 类是 ES6 引入的一个语法糖,其本身也是通过原型链模拟的,并不是如 C++
,java
等面向对象语言实现了类。
-
实例属性
// Class 之前 function Animal(type) { this.type = type } let obj = new Animal('dog') obj.type // dog // Class 之后 class Animal { constructor(type) { this.type = type } } let obj = new Animal('dog') obj.type // dog // Class 属性拦截 class Animal { constructor(type) { this._type = type } set type(value) { this._type = value + ' is running' } get type() { return this._type } } let obj = new Animal('dog') obj.type // dog obj.type = 'cat' obj.type // cat is running
-
实例方法
// Class 之前 function Animal() { // 实例属性 } Animal.prototype.run = function () { return 'running' } let obj = new Animal() obj.run() // running // Class 之后 class Animal { constructor() { // 实例属性 } run() { return 'running' } } let obj = new Animal() obj.run() // running
-
静态属性
// Class 之前 function Animal() { // 实例属性 } Animal.line = 'friend' Animal.line // friend // Class 之后 class Animal { static line = 'friend' } Animal.line // // friend
-
静态方法
// Class 之前 function Animal() { // 实例属性 } Animal.run = function () { return 'running' } Animal.run() // running // Class 之后 class Animal { static run() { return 'running' } } Animal.run() // running
-
私有属性和私有方法
实例属性和静态属性均为公有属性,实例方法和静态方式均属于公有方法。私有属性和方法只在类内部可以访问,外部无法访问。
class Foo { #a; #b; constructor(a, b) { this.#a = a; this.#b = b; } #sum() { return this.#a + this.#b; } } Foo.#a // SyntaxError: Private field '#a' must be declared in an enclosing class Foo.#sum() // SyntaxError: Private field '#sum' must be declared in an enclosing class let obj = new Foo() obj.#a // SyntaxError: Private field '#a' must be declared in an enclosing class obj.#sum // SyntaxError: Private field '#sum' must be declared in an enclosing class
-
继承
子类继承父类,会继承父类的实例属性和方法,静态属性及方法。
class Father { static fs = 'father' constructor(fx, fy) { this.fx = fx this.fy = fy } static getFatherName() { return Father.fs } fsum() { return this.fx + this.fy } } class Child extends Father { static s = 'child' constructor(fx, fy, x, y) { super(fx, fy) this.x = x this.y = y } static getChildName() { return Child.s } sum() { return this.x + this.y } } // 静态 Child.s // child Child.getChildName() // child // 静态继承 Child.fs // father Child.getFatherName() // father // 实例 let obj = new Child(1,2,3,4) obj.x // 3 obj.y // 4 obj.sum() // 7 // 实例继承 obj.fx // 1 obj.fy // 2 obj.fsum() // 3
通过
super
和extends
可以完成静态属性和方法以及实例属性和方法的继承。私有属性和方法的继承需要借助实例方法进行读写操作。注意:
super
在构造函数中调用,必须在重写实例属性之前,其目的是初始化一个继承父类的this
对象。super
可以做父类构造函数,也可以做父类原型对象使用。class Father { #f = 'father' getF() { return this.#f } setF(value) { this.#f += value } } class Child extends Father { #c = 'child' constructor() { super() } getCF() { return this.#c + ' & ' + this.getF() } setCF(c,f) { this.#c += c this.setF(f) } } let obj = new Child() obj.setCF(' is C', ' is F') obj.getCF() // child is C & father is F
注意:class 继承实现为
Child.__proto__ === Father
和Child.prototype.__proto__ === Father.prototype
双链继承。
后记
ES6 扩展到此结束,其中还有 ES6 拓展的异步编程优化为在本篇体现,主要考虑是异步编程优化方案比较多,其中也涉及到微任务相关,准备单独开篇介绍,主要介绍 Promise,async await,generator 等。
本文来自博客园,作者:深巷酒,转载请注明原文链接:https://www.cnblogs.com/huangminghua/p/17273645.html