摘要:但是,它是用構(gòu)造器調(diào)用來(lái)這樣做的。本質(zhì)是因?yàn)樽尯瘮?shù)調(diào)用變成了構(gòu)造器調(diào)用假設(shè)有一個(gè)需要繼承的一個(gè)類(lèi)型添加一個(gè)屬性如何檢查類(lèi)繼承自省錯(cuò)誤方法只能查詢(xún)的祖先。
名詞解析
字面量
對(duì)象字面量 var a = {};
數(shù)組字面量 var arr = [1,2,3];
正則表達(dá)式字面量 var reg = /[a-z]/g;
屬性描述符
configurable:fasle是一個(gè)單向操作,同時(shí)阻止使用 delete 操作符移除既存屬性的能力。
enumerable控制是否在屬性枚舉操作中出現(xiàn),比如 for..in 循環(huán)。
writeable:false時(shí),會(huì)阻止同名屬性在 [[Prototype]] 鏈的低層被創(chuàng)建(遮蔽)。不能使用 = 賦值,而必須使用 Object.defineProperty(..)
如果一個(gè) foo 在 [[Prototype]] 鏈的高層某處被找到,而且它是一個(gè) setter,那么這個(gè) setter 總是被調(diào)用。沒(méi)有 foo 會(huì)被添加到(也就是遮蔽在)myObject 上,這個(gè) foo setter 也不會(huì)被重定義。
對(duì)象常量: writable:false 與 configurable:false 組合
防止擴(kuò)展: Object.preventExtensions(..)
封印: Object.seal(..) 等同于 防止擴(kuò)展+configurable:false
凍結(jié): Object.freeze(..) 等同于 封印+writable:false
例:創(chuàng)建一個(gè)可寫(xiě)的,可枚舉的,可配置的屬性p
var o2 = {}; Object.defineProperties(o2, { "p": {value: 2, writable: true, enumerable: true, configurable: true }, }); Object.getOwnPropertyDescriptor( o2, "p" );
o2 = Object.create({}, { p: {value: 2, writable: true, enumerable: true, configurable: true }, }); o2.hasOwnProperty( "p" )原型
prototype(顯式原型)
所有的函數(shù)默認(rèn)都會(huì)得到一個(gè)公有的,不可枚舉的屬性,稱(chēng)為 prototype,它可以指向任意的對(duì)象。__proto__是每個(gè)對(duì)象都有的一個(gè)屬性,而prototype是函數(shù)才會(huì)有的屬性。(通過(guò)Function.prototype.bind方法構(gòu)造出來(lái)的函數(shù)是個(gè)例外,它沒(méi)有prototype屬性。)
[[Prototype]](隱式原型,非標(biāo)準(zhǔn)寫(xiě)法__proto__)
如果默認(rèn)的 [[Get]] 操作不能直接在對(duì)象上找到被請(qǐng)求的屬性,那么它會(huì)沿著對(duì)象的 [[Prototype]] 鏈 繼續(xù)處理。
所有的對(duì)象(包括函數(shù))都有一個(gè)私有屬性(b.__proto__)指向它的原型對(duì)象(Foo.prototype)。該原型對(duì)象也有一個(gè)自己的原型對(duì)象(Object.__proto__) ,層層向上直到一個(gè)對(duì)象的原型對(duì)象為null。其中,下面3種隱式原型表述在本文中相同
Person.__proto__ 非標(biāo)準(zhǔn)
Object.getPrototypeOf(Person) 標(biāo)準(zhǔn)
[[Prototype]] 概念
原型圖解 1.首先了解內(nèi)置對(duì)象基本內(nèi)置對(duì)象 String ,Number ,Boolean ,Object ,Function ,Array
其他內(nèi)置對(duì)象 Date,RegExp,Error
這些對(duì)象的[[Prototype]]都指向Function.prototype(Object.getPrototypeOf(String) === Function.prototype)
Function.prototype是唯一一個(gè)typeof Xxx.prototype==="function"的prototype。其它的構(gòu)造器的prototype都是"object".
Object.prototype.constructor === Object Object.prototype.__proto__ === null Function.prototype.constructor === Function Function.prototype.__proto__ === Object.prototype2.其次試著定義一個(gè)函數(shù),看看發(fā)生什么
定義一個(gè)函數(shù)(function Foo)會(huì)創(chuàng)建原型對(duì)象(Object Foo.prototype)。
constructor(構(gòu)造器) 是在前面 用 new 關(guān)鍵字調(diào)用的任何函數(shù)。
function Foo(){} Foo.__proto__ === Function.prototype Foo.prototype.constructor === Foo//function Foo(){} Foo.prototype.__proto__ === Object.prototype3.接著new一下,也許應(yīng)該叫委托
new會(huì)沿者 [[Prototype]] 鏈向上找到.constructor
new Foo() 得到一個(gè)新對(duì)象(我們叫他 a),這個(gè)新對(duì)象 a 內(nèi)部地被 [[Prototype]] 鏈接至 Foo.prototype 對(duì)象。
var b = new Foo()//new作用:構(gòu)建一個(gè)被鏈接到另一個(gè)對(duì)象的對(duì)象,外加這個(gè)函數(shù)要做的其他任何事。 b.__proto__===Foo.prototype4.instanceof 和原型鏈?zhǔn)裁搓P(guān)系
object instanceof constructor用于測(cè)試constructor.prototype是否出現(xiàn)在object的原型鏈中的任何位置。
//像這樣 object.__proto__===constructor.prototype? object.__proto__.__proto__===constructor.prototype? object.__proto__.__proto__.__proto__===constructor.prototype? ...
b instanceof Foo//true b instanceof Object//true Foo.prototype.isPrototypeOf(b)//true Object.prototype.isPrototypeOf(b)//true Foo.prototype = {}; b instanceof Foo//false b instanceof Object//true Foo.prototype.isPrototypeOf(b)//false Object.prototype.isPrototypeOf(b)//true‘類(lèi)’與‘繼承’ ES5
大家模擬了很多繼承的方法,但本質(zhì)上都是兩種的變體:
1.原型繼承原型繼承,本質(zhì)是兩個(gè)對(duì)象間建立鏈接,一個(gè)對(duì)象將對(duì)屬性/函數(shù)的訪問(wèn) 委托 到另一個(gè)對(duì)象上。
1.1使用 new
// 假設(shè)有一個(gè)需要繼承的一個(gè)類(lèi)型 Animal function Cat() {} Cat.prototype = new Animal // 添加一個(gè)屬性 Cat.prototype.name = "cat"
Bar.prototype = new Foo() 確實(shí) 創(chuàng)建了一個(gè)新的對(duì)象,這個(gè)新對(duì)象也的確鏈接到了我們希望的Foo.prototype。但是,它是用 Foo(..) “構(gòu)造器調(diào)用”來(lái)這樣做的。如果這個(gè)函數(shù)有任何副作用(比如logging,改變狀態(tài),注冊(cè)其他對(duì)象,向 this添加數(shù)據(jù)屬性,等等),這些副作用就會(huì)在鏈接時(shí)發(fā)生(而且很可能是對(duì)錯(cuò)誤的對(duì)象!),而不是像可能希望的那樣,僅最終在 Bar()的“后裔”被創(chuàng)建時(shí)發(fā)生。于是,我們剩下的選擇就是使用 Object.create(..) 來(lái)制造一個(gè)新對(duì)象,這個(gè)對(duì)象被正確地鏈接,而且沒(méi)有調(diào)用 Foo(..)時(shí)所產(chǎn)生的副作用。一個(gè)輕微的缺點(diǎn)是,我們不得不創(chuàng)建新對(duì)象,并把舊的扔掉,而不是修改提供給我們的默認(rèn)既存對(duì)象。
1.2使用Object.create()
function Shape() {this.x = 0;this.y = 0;}// 父類(lèi) Shape.prototype.move = function(x, y) {this.x += x;this.y += y;};// 父類(lèi)的方法 function Rectangle() {Shape.call(this);} //子類(lèi) Rectangle.prototype = Object.create(Shape.prototype);// 子類(lèi)續(xù)承父類(lèi) Rectangle.prototype.constructor = Rectangle; var rect = new Rectangle(); rect instanceof Rectangle; // true rect instanceof Shape; // true
Object.create()做了什么?
Object.create=function (o){ function F() {} F.prototype = o; return new F(); }
兩種操作將Bar.prototype 鏈接至 Foo.prototype:
// ES6 以前 // 扔掉默認(rèn)既存的 `Bar.prototype` Bar.prototype = Object.create( Foo.prototype ); // ES6+ // 修改既存的 `Bar.prototype` Object.setPrototypeOf( Bar.prototype, Foo.prototype );2.構(gòu)造繼承
構(gòu)造繼承,為了符合表面上我們用 new 調(diào)用它,而且我們觀察到它“構(gòu)建”了一個(gè)對(duì)象。
本質(zhì)是因?yàn)?new 讓函數(shù)調(diào)用變成了“構(gòu)造器調(diào)用”
// 假設(shè)有一個(gè)需要繼承的一個(gè)類(lèi)型 Animal function Cat(name){ Animal.call(this) // 添加一個(gè)屬性 this.name = name || "cat" }如何 檢查“類(lèi)”繼承/自省:
錯(cuò)誤方法:a instanceof Foo
instanceof 只能查詢(xún) a 的“祖先”。
勉強(qiáng)正確方法:用來(lái)檢查 o1 是否關(guān)聯(lián)到(委托至)o2 的幫助函數(shù)
function isRelatedTo(o1, o2) { function F(){} F.prototype = o2; return o1 instanceof F; }
正確方法:Foo.prototype.isPrototypeOf( a )
isPrototypeOf(..) 回答的問(wèn)題是:在 a 的整個(gè) [[Prototype]] 鏈中,F(xiàn)oo.prototype 出現(xiàn)過(guò)嗎?
function A(name){ this.name=name; } A.prototype.sayName=function(){ console.log(this.name); } function B(age){ this.age=age; }
//原型繼承 B.prototype=new A("mbj"); //被B的實(shí)例共享 var foo=new B(18); foo.age; //18,age是本身攜帶的屬性 foo.name; //mbj,等價(jià)于foo.__proto__.name foo.sayName(); //mbj,等價(jià)于foo.__proto__.proto__.sayName() foo.toString(); //"[object Object]",等價(jià)于foo.__proto__.__proto__.__proto__.toString(); //構(gòu)造函數(shù)繼承
原型繼承缺點(diǎn):
1.所有子類(lèi)共享父類(lèi)實(shí)例,如果某一個(gè)子類(lèi)修改了父類(lèi),其他的子類(lèi)在繼承的時(shí)候,會(huì)造成意想不到的后果。
2.構(gòu)造子類(lèi)實(shí)例的時(shí)候,不能給父類(lèi)傳遞參數(shù)。
//構(gòu)造函數(shù)繼承,避免了原型繼承缺點(diǎn) function B(age,name){ this.age=age;A.call(this,name); } var foo=new B(18,"wmy"); foo.name; //wmy foo.age; //18 foo.sayName(); //undefined
構(gòu)造函數(shù)繼承缺點(diǎn):
1.父類(lèi)的prototype中的函數(shù)不能復(fù)用
//原型繼承+構(gòu)造函數(shù)繼承 function B(age,name){ this.age=age;A.call(this,name); } B.prototype=new A("mbj"); var foo=new B(18,"wmy"); foo.name; //wmy foo.age; //18 foo.sayName(); //wmy
結(jié)合了上述兩種方式的優(yōu)點(diǎn),但占用空間更大。
ES6在 ES2015/ES6 中引入了class關(guān)鍵字,但只是語(yǔ)法糖,JavaScript 仍然是基于原型的。
注意:函數(shù)聲明會(huì)提升,類(lèi)聲明不會(huì)。
//類(lèi)聲明 class Rectangle { constructor(height, width,x, y) {this.height = height,this.width = width,this.x = x,this.y = y;} get area() {return this.calcArea()} calcArea() {return this.height * this.width;} static distance(a, b) {return Math.hypot(a.x - b.x, a.y - b.y);} } const square1 = new Rectangle(10, 10,5,5); const square2 = new Rectangle(10, 10,6,6); console.log(square1.area); console.log(Rectangle.distance(square1 , square2 )); //類(lèi)表達(dá)式 let Rectangle = class Rectangle {constructor() {}};
extend 實(shí)現(xiàn)繼承
class Rectangle extends Shape { move() { super.move();//super 關(guān)鍵字用于調(diào)用對(duì)象的父對(duì)象上的函數(shù) } }
常規(guī)(非可構(gòu)造)對(duì)象 實(shí)現(xiàn)繼承
var Animal = {}; class Cat {} Object.setPrototypeOf(Cat.prototype, Animal);
混入(Mixin)
Object.assign(target, ...sources) 方法只會(huì)拷貝源對(duì)象(...sources)自身的并且可枚舉的屬性到目標(biāo)對(duì)象(target)。
function MyClass() { SuperClass.call(this) OtherSuperClass.call(this) } // 繼承一個(gè)類(lèi) MyClass.prototype = Object.create(SuperClass.prototype) // 混合其它 Object.assign(MyClass.prototype, OtherSuperClass.prototype) //Object.assign 會(huì)把 OtherSuperClass原型上的函數(shù)拷貝到 MyClass原型上, //使 MyClass 的所有實(shí)例都可用 OtherSuperClass 的方法 // 重新指定constructor MyClass.prototype.constructor = MyClass MyClass.prototype.myMethod = function() {}// do a thing
當(dāng)繼承的函數(shù)被調(diào)用時(shí),this 指向的是當(dāng)前繼承的對(duì)象,而不是繼承的函數(shù)所在的原型對(duì)象。
var o = { a: 2, m: function(){ return this.a + 1; } }; var p = Object.create(o); // p是一個(gè)繼承自 o 的對(duì)象 p.a = 4; // 創(chuàng)建 p 的自身屬性 a console.log(p.m()); // 5
派生
class MyArray extends Array { static get [Symbol.species]() { return Array; } }面向委托的設(shè)計(jì)
var Widget = { init: function(width,height){ this.width = width || 50; this.height = height || 50; this.$elem = null; }, insert: function($where){ if (this.$elem) { this.$elem.css( { width: this.width + "px", height: this.height + "px" } ).appendTo( $where ); } } }; var Button = Object.create( Widget ); Button.setup = function(width,height,label){ // delegated call this.init( width, height ); this.label = label || "Default"; this.$elem = $( "
Object.create 和 new 區(qū)別
js中__proto__和prototype的區(qū)別和關(guān)系?
你不懂JS: this 與對(duì)象原型
行為委托
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/99779.html
摘要:但是,它是用構(gòu)造器調(diào)用來(lái)這樣做的。本質(zhì)是因?yàn)樽尯瘮?shù)調(diào)用變成了構(gòu)造器調(diào)用假設(shè)有一個(gè)需要繼承的一個(gè)類(lèi)型添加一個(gè)屬性如何檢查類(lèi)繼承自省錯(cuò)誤方法只能查詢(xún)的祖先。 名詞解析 字面量對(duì)象字面量 var a = {};數(shù)組字面量 var arr = [1,2,3];正則表達(dá)式字面量 var reg = /[a-z]/g; 屬性描述符 configurable:fasle是一個(gè)單向操作,同時(shí)阻止使...
摘要:但是,它是用構(gòu)造器調(diào)用來(lái)這樣做的。本質(zhì)是因?yàn)樽尯瘮?shù)調(diào)用變成了構(gòu)造器調(diào)用假設(shè)有一個(gè)需要繼承的一個(gè)類(lèi)型添加一個(gè)屬性如何檢查類(lèi)繼承自省錯(cuò)誤方法只能查詢(xún)的祖先。 名詞解析 字面量對(duì)象字面量 var a = {};數(shù)組字面量 var arr = [1,2,3];正則表達(dá)式字面量 var reg = /[a-z]/g; 屬性描述符 configurable:fasle是一個(gè)單向操作,同時(shí)阻止使...
摘要:捕獲所有參數(shù)綁定當(dāng)一個(gè)函數(shù)用作構(gòu)造函數(shù)時(shí)使用關(guān)鍵字,它的被綁定到正在構(gòu)造的新對(duì)象。使用來(lái)調(diào)用函數(shù),或者說(shuō)發(fā)生構(gòu)造函數(shù)調(diào)用時(shí),會(huì)自動(dòng)執(zhí)行下面的操作你不知道的創(chuàng)建或者說(shuō)構(gòu)造一個(gè)全新的對(duì)象。在箭頭函數(shù)中,與封閉詞法上下文的保持一致。 this 實(shí)際上是在函數(shù)被調(diào)用時(shí)發(fā)生的綁定,它指向什么完全取決于函數(shù)的調(diào)用位置(也就是函數(shù)的調(diào)用方法)。 四條規(guī)則:(你不知道的JS) 1. 默認(rèn)綁定 func...
摘要:捕獲所有參數(shù)綁定當(dāng)一個(gè)函數(shù)用作構(gòu)造函數(shù)時(shí)使用關(guān)鍵字,它的被綁定到正在構(gòu)造的新對(duì)象。使用來(lái)調(diào)用函數(shù),或者說(shuō)發(fā)生構(gòu)造函數(shù)調(diào)用時(shí),會(huì)自動(dòng)執(zhí)行下面的操作你不知道的創(chuàng)建或者說(shuō)構(gòu)造一個(gè)全新的對(duì)象。在箭頭函數(shù)中,與封閉詞法上下文的保持一致。 this 實(shí)際上是在函數(shù)被調(diào)用時(shí)發(fā)生的綁定,它指向什么完全取決于函數(shù)的調(diào)用位置(也就是函數(shù)的調(diào)用方法)。 四條規(guī)則:(你不知道的JS) 1. 默認(rèn)綁定 func...
閱讀 1852·2021-08-19 11:12
閱讀 1426·2021-07-25 21:37
閱讀 990·2019-08-30 14:07
閱讀 1269·2019-08-30 13:12
閱讀 653·2019-08-30 11:00
閱讀 3530·2019-08-29 16:28
閱讀 995·2019-08-29 15:33
閱讀 2972·2019-08-26 13:40