您好,登錄后才能下訂單哦!
這篇文章將為大家詳細講解有關JavaScript原型和原型鏈的案例分析,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。
1、題目
如何準確判斷一個變量是數(shù)組
寫一個原型鏈繼承的例子
繼承實現(xiàn)的其他方式
es6 實現(xiàn)繼承的底層原理是什么
描述new一個對象的過程
zepto及其他源碼中如何使用原型鏈
2.知識點
2.1 構造函數(shù)
特點:以大寫字母開頭
function Foo(name,age){ //var obj = {} //this = {} this.name = name; this.age = age; this.class = 'class1' // return this } var f1 = new Foo('liming',19);
擴展
var o = {} 是 var o = new Object() 的語法糖
var a = [] 是 var a = new Array() 的語法糖
function Foo(){} 相當于 var Foo = new Function(){}
2.2 原型規(guī)則
五條規(guī)則:
1.所有引用類型(對象,數(shù)組,函數(shù))都具有對象特性,即可以自由擴展屬性
2.所有引用類型(對象,數(shù)組,函數(shù))都具有一個__proto__(隱式原型)屬性,是一個普通對象
3.所有的函數(shù)都具有prototype(顯式原型)屬性,也是一個普通對象
4.所有引用類型(對象,數(shù)組,函數(shù))__proto__值指向它構造函數(shù)的prototype
5.當試圖得到一個對象的屬性時,如果變量本身沒有這個屬性,則會去他的__proto__中去找
for (var key in object) { //高級瀏覽器中已經(jīng)屏蔽了來自原型的屬性 //建議加上判斷保證程序的健壯性 if (object.hasOwnProperty(key)) { console.log(object[key]); } }
2.3 原型鏈
obj.__ proto . proto . proto __ ...
Object.prototype === null
instanceof 用于判斷引用類型屬于哪個構造函數(shù)
obj instanceob Foo
實際意義:判斷 Foo.prototype 在不在 obj的原型鏈上
3.題目解答
3.1 如何準確判斷一個變量是數(shù)組
arr instanceof Array
3.2 寫一個原型鏈繼承的例子
封裝dom查詢
function Elem(id){ this.elem = document.getElementById(id); }; Elem.prototype.html = function(val){ var elem = this.elem; if (val) { elem.innerHTML = val; return this; }else{ return elem.innerHTML; } } Elem.prototype.on = function(type,fun){ var elem = this.elem; elem.addEventListener(type,fun); return this; } var p1 = new Elem('id1'); p1.html("test").on('click',function(){ console.log('點擊'); })
3.3 繼承實現(xiàn)的其他方式
3.3.1 原型繼承
var obj = { 0:'a', 1:'b', arr:[1] } function Foo(arr2){ this.arr2 = [1] } Foo.prototype = obj; var foo1 = new Foo(); var foo2 = new Foo(); foo1.arr.push(2); foo1.arr2.push(2); console.log(foo2.arr); //[1,2] console.log(foo2.arr2); //[1]
優(yōu)點:實現(xiàn)簡單
缺點:
1.無法向父類構造函數(shù)傳參
2.同時new兩個對象時改變一個對象的原型中的引用類型的屬性時,另一個對象的該屬性也會修改。因為來自原型對象的引用屬性是所有實例共享的。
function Super(b){ this.b = b; this.fun = function(){} } function Foo(a,b){ this.a = a; Super.call(this,b); } var foo1 = new Foo(1,2); console.log(foo1.b);
優(yōu)點:可以向父類傳參,子類不會共享父類的引用屬性
缺點:無法實現(xiàn)函數(shù)復用,每個子類都有新的fun,太多了就會影響性能,不能繼承父類的原型對象。
function Super(){ // 只在此處聲明基本屬性和引用屬性 this.val = 1; this.arr = [1]; } // 在此處聲明函數(shù) Super.prototype.fun1 = function(){}; Super.prototype.fun2 = function(){}; //Super.prototype.fun3... function Sub(){ Super.call(this); // 核心 // ... } Sub.prototype = new Super();
優(yōu)點:不存在引用屬性共享問題,可傳參,函數(shù)可復用
缺點:父類的屬性會被實例化兩次,獲取不到真正實例父類(無法區(qū)分實例是父類創(chuàng)建還是父類創(chuàng)建的)
優(yōu)化:
function Super(b){ this.b = b; this.fun = function(){} } Super.prototype.c = function(){console.log(1111)} function Foo(a,b){ this.a = a; Super.call(this,b); } Foo.prototype = Super.prototype; //修復構造函數(shù): var foo1 = new Foo(1,2);
缺點:無法區(qū)分實例是父類創(chuàng)建還是子類創(chuàng)建的
function Super(b){ this.b = b; } Super.prototype.c = function(){console.log(1111)} function Foo(a,b){ this.a = a; Super.call(this,b); } var f = new Function(); f.prototype = Super.prototype; Foo.prototype = new f(); //等同于 Foo.prototype = Object.create(Super.prototype); var foo1 = new Foo(1,2);
對父類的prototype進行一次寄生,即包裝成一個空對象的prototype,再把這個對象實例化出來作為子類的peototype。
缺點:無法區(qū)分實例是父類創(chuàng)建還是子類創(chuàng)建的
可以添加以下代碼:
Foo.prototype.constructor = Foo
這種解決方法不能用于上面的組合優(yōu)化方法,因為子類父類引用的是同一個原型對象,修改會同時修改。
總結:
繼承主要是實現(xiàn)子類對父類方法,屬性的復用。
來自原型對象的引用屬性是所有實例共享的,所以我們要避免從原型中繼承屬性。
在構造函數(shù)中通過call函數(shù)可以繼承父類構造函數(shù)的屬性和方法,但是通過這種方式實例化出來的實例會將父類方法多次存儲,影響性能。
通過組合繼承我們使用call繼承屬性,使用原型繼承方法,可以解決以上兩個問題,但是通過這種方式實例化出來的對象會存儲兩份父類構造函數(shù)中的屬性。
用父類的原型構造一個新對象作為子類的原型,就解決了多次存儲的問題,所以最終的寄生組合繼承就是最佳繼承方式,它的缺點就是書寫起來比較麻煩。
function inherits(ctor, superCtor) { ctor.super_ = superCtor; ctor.prototype = Object.create(superCtor.prototype, { constructor: { value: ctor, enumerable: false, writable: true, configurable: true } }); }; function Stream(){ //... } function OutgoingMessage() { Stream.call(this); //... } inherits(OutgoingMessage, Stream); OutgoingMessage.prototype.setTimeout = ...
以上是寄生組合繼承的一個實例。
1.在OutgoingMessage構造函數(shù)中通過call繼承Stream構造中的屬性。
2.調(diào)用inherits方法繼承Stream原型中的屬性。
3.擴展OutgoingMessage自身原型的函數(shù)。
inherits方法中使用了Object.create方法,該方法的作用是通過指定的原型對象和屬性創(chuàng)建一個新的對象。
ctor.prototype=Object.create(superCtor.prototype,{.....});
該方法實際上就做了我們上面寄生組合繼承中的工作
var f = new Function(); f.prototype =superCtor.prototype; return new f();
后面的參數(shù)是給原型對象添加屬性,可選屬性(非必填),即把自身作為新創(chuàng)建對象的構造函數(shù)。
value: 表示constructor 的屬性值; writable: 表示constructor 的屬性值是否可寫;[默認為: false] enumerable: 表示屬性constructor 是否可以被枚舉;[默認為: false] configurable: 表示屬性constructor 是否可以被配置,例如 對obj.a做 delete操作是否允許;[默認為: false]
參考我這篇文章:https://segmentfault.com/a/11...
創(chuàng)建一個對象
{}._proto_ = 構造函數(shù).prototype
this指向這個對象
執(zhí)行代碼即對this賦值
返回this
var Zepto = (function(){ var $,zepto = {} // ...省略N行代碼... $ = function(selector, context){ return zepto.init(selector, context) } zepto.init = function(selector, context) { var dom // 針對參數(shù)情況,分別對dom賦值 // 最終調(diào)用 zepto.Z 返回的數(shù)據(jù) return zepto.Z(dom, selector) } fnction Z(dom, selector) { var i, len = dom ? dom.length : 0 for (i = 0; i < len; i++) this[i] = dom[i] this.length = len this.selector = selector || '' } zepto.Z = function(dom, selector) { return new Z(dom, selector) } $.fn = { // 里面有若干個工具函數(shù) } zepto.Z.prototype = Z.prototype = $.fn // ...省略N行代碼... return $ })() window.Zepto = Zepto window.$ === undefined && (window.$ = Zepto)
關于JavaScript原型和原型鏈的案例分析就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。