💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
[TOC] >[success] # 更强大的原型 原型是在`JS`中进行继承的基础, `ES6` 则在继续让原型更强大。早期的`JS`版本对原型的使用 有严重限制,然而随着语言的成熟,开发者也越来越熟悉原型的工作机制,因此他们明显希 望能对原型有更多控制权,并能更方便地使用它。于是 ES6 就给原型引入了一些改进。 >[success] ## ES6新增对象方法 ~~~ 首先介绍一下,下面几个对象的方法: ~~~ <br/> >[success] ### Object.setPrototypeOf() ~~~ 'Object.setPrototypeOf()' 方法'设置一个指定的对象的原型'(即, 内部[[Prototype]]属性) 到另一个对象或 null,和'Object.create()'类似,两者的区别:'Object.create()'是新建一个对象 可以为这个对象指定另一个对象的'原型','Object.setPrototypeOf()' 是改变源对象,为其指定另一个 对象的'原型' 语法: Object.setPrototypeOf(obj, prototype) 参数: 1. 'obj':要设置其原型的对象。 2. 'prototype' : 该对象的新原型(一个对象 或 null) ~~~ 1. 简单例子 ~~~ function fun1 () { } // 下面的操作,相当于在这个原型链里面新增了一些方法 // fun1.prototype.age = '18'; let obj1 = new fun1() Object.setPrototypeOf(obj1, { age: '18' }) console.log(obj1) ~~~ <br/> >[success] ### Object.defineProperty() ~~~ 'Object.defineProperty()'的作用就是直接在一个对象上'定义一个新属性',或者'修改一个已经存在的属性' 语法: Object.defineProperty(obj, prop, desc) 参数: 1. 'obj' : 需要定义属性的当前对象 2. 'prop' :当前需要定义的属性名 3. 'desc': 属性描述符 ~~~ 1. 添加(修改)属性 ~~~ var o = {}; // 创建一个新对象 // 在对象中添加一个属性与数据描述符的示例 Object.defineProperty(o, "a", { value : 37, writable : true, enumerable : true, configurable : true }); ~~~ <br/> >[success] ### Object.defineProperties() 对象新增(修改)属性,返回对象 ~~~ 'Object.defineProperties()'方法直接在一个对象上定义'新的属性或修改现有属性',并'返回该对象'。 ~~~ ~~~ 语法:Object.defineProperties(obj, props) 参数: 'obj' 需要定义属性的当前对象 'prop' 当前需要定义的属性名 ~~~ <br/> >[success] ### Object.getOwnPropertyDescriptor 获取对象属性的属性描述符 ~~~ let obj = { aa:'111', bb:'222' } console.log(Object.getOwnPropertyDescriptor(obj,'aa')) // {"aa":{"value":"111","writable":true,"enumerable":true,"configurable":true}} 语法:Object.getOwnPropertyDescriptor(obj, props) 参数: 'obj' 要在其中查找属性的对象 'prop' Symbol要检索其描述的名称或属性。 ~~~ <br/> >[success] ### Object.getOwnPropertyDescriptors 获取对象全部属性的属性描述符 ~~~ let obj = { aa:'111', bb:'222' } console.log(Object.getOwnPropertyDescriptors(obj,'aa')) // {"aa":{"value":"111","writable":true,"enumerable":true,"configurable":true},"bb":{"value":"222","writable":true,"enumerable":true,"configurable":true}} 语法:Object.getOwnPropertyDescriptors(obj) 参数: 'obj' 要查找属性的对象 ~~~ <br/> >[success] ## 修改原型 ~~~ let person = { getGreeting() { return "Hello" } } let dog = { getGreeting() { return "Woof" } } // 原型为 person let friend = Object.create(person) console.log(friend.getGreeting()) // "Hello" console.log(Object.getPrototypeOf(friend) === person) // true // 将原型设置为 dog Object.setPrototypeOf(friend, dog) console.log(friend.getGreeting()) // "Woof" console.log(Object.getPrototypeOf(friend) === dog) // true ~~~ <br/> >[success] ## 使用 super 引用的简单原型访问 [参考文章:js中的super的使用](http://www.fly63.com/article/detial/4207) ~~~ 1. 'this关键词'指向函'数所在的当前对象' 2. 'super'指向的是'当前对象的原型对象' ~~~ <br/> >[success] ### ES5原型访问 ~~~ // 原型对象1 const person = { name: '小明1', test(){ console.log('我是原型的方法1') } } // 基础原型对象 const man = { name: '小明', test(){ console.log('我是自身的方法') }, getProtoAttr() { // 调用原型属性 return Object.getPrototypeOf(this).name }, getProtoFun(){ // 调用原型方法 return Object.getPrototypeOf(this).test.call(this) } } // 修改man对象的原型对象为person Object.setPrototypeOf(man, person) // 调用自身的私有属性以及方法 console.log(man.name) // 小明 man.test() // 我是自身的方法 // 调用原型链的属性和方法 console.log(man.getProtoAttr()) // 小明1 man.getProtoFun() // 我是原型的方法 ~~~ <br/> >[success] ### ES6原型访问 ~~~ // 原型对象1 const person = { name: '小明1', test(){ console.log('我是原型的方法1') } } // 基础原型对象 const man = { name: '小明', test(){ console.log('我是自身的方法') }, getProtoAttr() { // 调用原型属性 return super.name // 注意这里 }, getProtoFun(){ // 调用原型方法 return super.test() // 注意这里 } } // 修改man对象的原型对象为person Object.setPrototypeOf(man, person) // 调用自身的私有属性以及方法 console.log(man.name) // 小明 man.test() // 我是自身的方法 // 调用原型链的属性和方法 console.log(man.getProtoAttr()) // 小明1 man.getProtoFun() // 我是原型的方法 ~~~ <br/> >[success] ### super中的this指向(易混淆) 1. super.name 指向的是原型对象 person 中的 name ,但是绑定的 this 还是当前的 man 对象。 ~~~ '属性重名': 自身'私有属性'和原型链属性'重名'时'super'先调用'自身私有属性',如果自身没有这个属性,'super'会自动去'原型链'找这个属性并且'调用'。 '方法重名','super'只会调用'原型链方法'不会调用本地的方法 const person = { age:'20多了', name(){ return this.age } } const man = { age:'18岁了', sayName(){ return super.name() } } // 为man对象设置原型对象 Object.setPrototypeOf( man, person ) // 调用原型链方法 console.log( man.sayName() ) // 18岁了 ~~~ 2. Object.getPrototypeOf(this).name 指向的是 person 的 name,绑定的 this 也是 person ~~~ const person = { age: '20多了', name() { return this.age } } const man = { age: '18岁了', sayName() { return Object.getPrototypeOf(this).name() } } // 为man对象设置原型对象 Object.setPrototypeOf(man, person) // 调用原型链方法 console.log(man.sayName()) //20多了 ~~~ 3.Object.getPrototypeOf(this).name.call(this)指向的是person的name,不过通过call改变了函数的执行上下文,所以this指向的还是man ~~~ const person = { age: '20多了', name() { return this.age } } const man = { age: '18岁了', sayName() { return Object.getPrototypeOf(this).name.call(this) } } // 为man对象设置原型对象 Object.setPrototypeOf(man, person) // 调用原型链方法 console.log(man.sayName()) //18岁了 ~~~ <br/> >[success] ### Class中的super 1. Class中的super(),它在这里表示父类的构造函数,用来新建父类的this对象 ,super()相当于Parent.prototype.constructor.call(this) ~~~ // 父类 class Demo { constructor(x, y) { this.x = x this.y = y } customSplit() { return [...this.y] } } // 子类继承父类 class Demo2 extends Demo { constructor(x, y) { // 如果不写constructor,js会自动生成一个空的constructor super(x, y) } customSplit() { return [...this.x] } task1() { // 调用父类(原型链)方法 return super.customSplit() } task2() { // 调用自身方法 return this.customSplit() } } let d = new Demo2('hello', 'world') console.log(d.task1()) //["w", "o", "r", "l", "d"] console.log(d.task2()) //["h", "e", "l", "l", "o"] ~~~ 2. 子类没有自己的this对象,而是继承父亲的this对象,然后进行加工。如果不调用super,子类就得不到this对象 ~~~ class Demo2 extends Demo { constructor(x, y) { this.x = x //this is not defined } } ~~~ ES5的继承,实质上是先创造子类的实例对象this,然后再将父类的方法添加到this上(Parent.call(this)). ES6的继承,需要先创建父类的this,子类调用super继承父类的this对象,然后再加工。 如果子类没有创建 constructor ,这个方法会被默认添加: ~~~ // 创建父类 class Demo{ constructor(x) { this.x = x; } } // 继承父类 class Demo2 extends Demo{} // 实例化对象 let d = new Demo2('hello'); d.x //hello ~~~ 3. super 在静态方法之中指向父类,在普通方法之中指向父类的原型对象。 可以理解成,没有实例化对象时候,使用静态的方法只能引静态的方法,实例化后原型的方法可以使用,并且原型方法权重高所以就引用了原型的方法 ~~~ // 父类 class Parent { // 静态方法 static myMethod(msg) { console.log('static', msg) } myMethod(msg) { console.log('instance', msg) } } // 子类继承父类 class Child extends Parent { // 静态方法 static myMethod(msg) { super.myMethod(msg) } myMethod(msg) { super.myMethod(msg) } } // 直接引用静态方法 Child.myMethod(1) // static 1 // 实例化子类对象 let child = new Child() child.myMethod(2) // instance 2 ~~~ <br/> >[warning] ### super 使用时需注意 ~~~ 'super'只可以在'简写方法'内使用,在'非简写'的情况下使用会报错 ~~~ ~~~ let man= { test: function () { // 语法错误 return super.test() } } ~~~