深入瞭解原型
說原型之前先說說物件,好像在工作中,物件用的挺多的,原型基本上沒有用。既然沒有用那我還要不要學習呢?思考了很久,還是學一學,萬一以後的工作用的著呢?領導常說,上一份工作是為下一份工作做準備的,所以在工作中要多學東西,不要因為暫時不用,而放棄學習,目光要放得長遠。既然這樣我就簡單學一學原型。說原型之前先溫習一下物件的相關知識
物件:屬性值的集合,屬性可以包括基本值,物件,或者函式
原型:在物件上面新增特定的屬性,是定義屬性的快捷方式,可以假想成 java
裡面的類 (ps:Vue
例項就是在 vue
物件上面進行原型擴充套件
瞭解原型之前,先簡單的介紹一下物件的建立
- 1.物件字面量
var person = {
name: "Nice,
age: 23
}
複製程式碼
優點:程式碼量少,給人封裝資料的感覺,也可以向函式傳遞大量可選引數;在實際開發中常用
new
操作符後跟Object
建構函式(幾乎不用)
var person=new Object();
person.name="Nice;
person.age=23;
複製程式碼
- 3.工廠模式(幾乎不用)
function createPerson(name,age){
var o=new Object();
o.name=name;
o.age=age;
return o;
}
var person=createPerson("Nice",23);
複製程式碼
用一個函式把建構函式包裹起來,再在該函式體內返回改該建構函式
- 4.建構函式模式(很少用)
function Person(name,age){
this.name=name;
this.age=age;
this.sayName=function(){
this.name;
}
}
var person=new Person("Nice",23);
複製程式碼
建構函式始終都應該以一個大寫字母開頭,建構函式它本身就是一個函式,如果沒有 new
1.建立一個新物件
2.將建構函式的作用域賦給新物件(
this
就職向這個新物件)
3.執行建構函式中的程式碼
4.返回新的物件
建構函式建立物件很方便,但也有他的問題:每個方法都要在每個例項上都要重新建立建立一遍,不同的例項的同名函式是不相等;因此建立了大量的重複程式碼(當你 new
一個構造器物件,上面的建構函式就執行一遍,每次都會新建一個 function
,會新開闢一個記憶體空間,每次都是指向一個新的堆的物件 ,這樣佔用記憶體消耗非常的大);即重複造輪子,我們希望的是程式碼儘可能的複用,這時候我們就需要原型(例項,建構函式,原型物件之間的關係請看下面)
原型所定義的屬性和功能會自動應用到物件的例項上(每個物件都有一份原型引用)
function Animal(name,age) {
//加一個 this 條件判斷,用 instanceof 來檢查自己是否被 new 呼叫
if (this instanceof Animal) {
this.name = name
this.age = age
}eles {
//以 new 遞迴呼叫自己來為物件建立正確的例項,目的保持沒有 new 和有new 的表現行為一致
return new Animal(name,age)
}
}
複製程式碼
所有函式在初始化的時候都有一個 prototype
屬性,該屬性的初始值是一個空物件,只有函式在作為建構函式的時候,prototype
屬性指向原型物件,這個物件包含所有例項共享的屬性和方法
所有原型物件會自動獲取一個 constructor
屬性,指向建構函式
由此可以看出,例項和建構函式之間沒有什麼直接的關係
function Person(){}
Person.prototype.dance=function(){}
function Ninja(){}
Ninja.prototype={dance:Person.prototype.dance}
複製程式碼
注意:Person.prototype
設定為一個物件字面量形式建立的新物件時,就切斷了原來物件的聯絡(即 constructor
屬性不在指向Person
)
如果 constructor
的值很重要可以向在新物件中設定:
造成這個原因是,例項和原型之間的鬆散連結關係,例項中的指標只指向原型,而不指向建構函式(可以看上面,原型,例項,建構函式直接的關係)
但是重設 constructor
屬性,會導致它的[[Enumerable]]
的特性被設為 true
,最好使用 Object.defineProperty()
Object.defineProperty(Person.prototype,'constructor',{
enumerable:false,
value:'',
writable:true
})
複製程式碼
Object.defineProperty()
: 這個方法接受三個引數,屬性所在的物件,屬性的名字,一個描述符物件;其中描述符物件的屬性的一個或者多個值(Configurable
,Enumerable
,Writable
,Value
,get
,set
),一旦configurable
定義為不可配置的(false
),就不能把它修改成可配置的,返回被傳遞的物件
Object.defineProperties()
:一次性可修改多個屬性,第一個引數是屬性物件,第二個引數是所要修改的資料屬性組成的集合(即要修改的資料物件),返回被傳遞的物件
Object.getOwnPropertyDescriptor()
:讀取屬性描述符;第一個引數是屬性所在的物件,第二個是要讀取其描述符的屬性名稱,返回一個物件
建構函式內部的繫結操作符優先順序永遠都高於在原型上繫結的操作符優先順序,在應用物件的一個屬性時,優先檢查該物件上本身是否擁有該屬性,如果有則直接返回,否則繼續在原型上面找,找不到就返回 undefined
。一般情況不會輕易去修改原型物件上的屬性,一旦修改就會出現各種問題;
判斷屬性是例項還是原型的幾種常用方法
hasOwnProperty()
:如果返回 true
,該屬性存在例項當中
person.hasOwnProperty('name');//返回 true,name 屬性在 person 例項當中
複製程式碼
in
操作符,只要返回 true
,該屬性就在物件中,也許是原型中,也許是例項當中
'name' in person //返回 true ,該屬性存在
複製程式碼
for-in
迴圈時,返回所有能夠通過物件訪問的,可列舉的屬性
Object.keys()
:返回物件上所有可列舉的例項屬性組成的字串陣列
function Person(name,age){
this.name=name;
this.age=age;
this.sayName=function(){
this.name;
}
}
Person.prototype.job=function(){
}
var person=new Person();
console.log(Object.keys(person));//返回["name", "age", "sayName"]
複製程式碼
instanceof
操作符真正的含義:檢查右邊的建構函式原型是否存在於操作符左邊的物件原型上的任何一個位置 object instanceof constructor
原型物件的問題
函式的原型是一個物件,所以有很多功能(屬性或者方法)可以通過賦值的方法到達繼承的目的,同時也可以定義新的方法;
因為原型物件上所有的屬性和方法是共享的,而對於屬於引用型別值的屬性來說,會直接修改原型物件上的屬性,造成 bug
,引一發而動全身
解決原型物件的問題
- 建構函式和原型模式相結合:引用型別值的屬性放在建構函式當中,其他共享的不會修改屬性放在原型物件上
2.動態原型模式:根據函式是否在建構函式中,而選擇性的新增到原型物件中去
3.寄生建構函式
長得和工廠模式一模一樣,不同的是,通過 new
操作符來呼叫
function Person(name,age){
var o=new Object();
o.name=name;
o.age=age;
return o;
}
var friend=new Person();
複製程式碼
class
實現繼承 class
底層的實現仍然是基於原型繼承(ES6語法) 建立類
class Ninja {//建立類名
constructor(name) {//定義建構函式。當使用關鍵字 new 呼叫類時。會呼叫這個建構函式
this.name = name
}
swingSword(){//定義一個Ninja 例項均可訪問的方法
return true
}
}
複製程式碼
繼承類
class Ninja extends Person {//extends 實現繼承
constructor(name.weapon) {
super(name)
this.weapon = weapon
}
wieldWeapon(){
return true
}
}
複製程式碼
期待我的續更吧,或許寫的有點糟糕,我是初學者,如有錯誤之處,請多多請教(sunseekers_)。掘金談技術,公眾號談生活(sunseekers)