[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()
}
}
~~~
- Javascript基礎篇
- Array數組
- 數組插入值
- filter()
- forEach()
- push()
- pop()
- unshift()
- shift()
- valueOf()
- 面向對象思想
- Javascript 面向對象編程(一):封裝
- Javascript面向對象編程(二):構造函數的繼承
- Javascript面向對象編程(三):非構造函數的繼承
- 解構
- 數組的解構賦值
- 對象的解構賦值
- 函數參數解構
- 字符串的解構賦值
- 數值和布爾值的解構賦值
- 圓括號問題
- 字符串.
- split()
- charAt()
- charCodeAt()
- concat()
- indexOf()
- lastIndexOf()
- match()
- replace()
- includes()
- 初識遞歸
- 渲染ul-li樹形結構
- 異步函數解決方案
- 1. callback回調函數
- 2. ES6 - Promise
- JavaScript高級程序設計(書)
- 在html中使用JavaScript
- script標簽的位置
- 延遲腳本
- 異步腳本
- <noscript>元素
- 基本概念
- 嚴格模式
- 變量詳解
- 數據類型
- typeof操作符
- undefined類型
- Null類型
- Boolean類型
- Number類型
- 深入了解ES6(書)
- var 、let 、 const
- 字符串與正則表達式
- 字符串
- 正則表達式
- 函數
- 函數形參默認值
- 使用不具名參數
- 函數構造器的增強能力
- 擴展運算符
- name屬性
- 明確函數的多重用途
- 塊級函數
- 箭頭函數
- 尾調用優化
- 擴展的對象功能
- 對象類別
- 對象字面量語法的擴展
- ES6對象新增方法
- 重復的對象屬性
- 自有屬性的枚舉順序
- 更強大的原型
- 解構:更方便的數據訪問
- 為什么要用解構?
- 對象解構
- 數組解構
- 混合解構
- 參數解構
- Symbol與Symbol屬性
- 創建Symbol
- Symbol的使用方法
- Symbol全局私有屬性
- Symbol與類型強制轉換
- Symbol屬性檢索
- Symbol的一些構造方法
- Set集合與Map集合
- Set集合
- Weak Set集合(弱引用Set集合)
- Map集合
- JS標準內置對象
- Object 構造函數及屬性
- Object 構造方法
- Symbol 內建對象類的函數及屬性
- Set 構造函數及屬性
- Weak Set 構造函數及屬性
- JS雜項
- 類數組對象
- Class類的理解和使用