摘要:創(chuàng)建一個新的對象即實例對象把新對象的指向后面構(gòu)造函數(shù)的原型對象。簡單來驗證一下等同與對象沒有原型對象的原型對像等同于構(gòu)造函數(shù)是等同于,構(gòu)造函數(shù)是七原型鏈的作用其實,原型鏈的根本作用就是為了屬性的讀取。
首先說一下,函數(shù)創(chuàng)建的相關(guān)知識
在JavaScript中,我們創(chuàng)建一個函數(shù)A(就是聲明一個函數(shù)), 那么 js引擎 就會用構(gòu)造函數(shù)Function來創(chuàng)建這個函數(shù)。所以,所有的函數(shù)的constructor屬性都指向 構(gòu)造函數(shù)Function。A.constructor === Function; //true(函數(shù)本身并沒有這個屬性,后面介紹。記住,這里是函數(shù),重點,要考,哈哈) 然后會在內(nèi)存中創(chuàng)建一個原型對象B,原型對象這個東西,看不見,摸不著,只能通過函數(shù)的 prototype 來獲取這個對象。( 即:prototype的屬性的值是這個對象 )。
再說一下,對象創(chuàng)建的相關(guān)知識對象的創(chuàng)建,有三種方式
1、字面量方式(又稱直接量)
啥是字面量呢?可以理解為,沒用new出來的都屬于字面量創(chuàng)建。
舉個栗子:
var c = {}; //c 是變量, {} 就是字面量了
字面量對象的創(chuàng)建過程跟函數(shù)的過程相似,當(dāng)你聲明一個對象字面量時,js引擎就會構(gòu)造函數(shù) Object 來創(chuàng)建這個對象,所以 效果等同 new Object()
2、構(gòu)造器方式
這個呢,就是用 new 創(chuàng)建。
栗子在此:
var c = new Object();
3、Object.create
這是個ES5中新增的方法,這個方法是創(chuàng)建一個新對象,把它的__proto__指向傳進來的函數(shù)或者對象。到底是怎么回事呢,下面,咱們來簡單實現(xiàn)下它的功能
function create(proto, options){ //proto表示傳入的函數(shù)或者對象,options表示給子類添加的屬性 var obj = {}; obj.__proto__ = proto; if(options == null){ return obj; } return Object.defineProperties(obj,options); //Object.defineProperties方法直接在一個對象上定義新的屬性或修改現(xiàn)有屬性,并返回該對象。所以這里就直接return了 }
檢驗一下:
var a = function(){}; //自制的create var b = create(a); console.log(b.__proto__ === a); //true; var c = create(a.prototype); console.log(c.__proto__ === a.prototype); //true //Object.create var d = create(a); console.log(d.__proto__ === a); //true; var e = create(a.prototype); console.log(e.__proto__ === a.prototype); //true
這里說明一下,你可以在別處看到的create的實現(xiàn)是這樣的:
//這里就簡化第二個參數(shù)了。就寫第一個參數(shù)的實現(xiàn) function create(proto){ function f(){}; f.prototype = proto; return new f(); }
這個是兼容的寫法,因為,__proto__ 屬性是非標(biāo)準(zhǔn)的,部分現(xiàn)在瀏覽器實現(xiàn)了該屬性。其實,你要是明白,new到底干了啥, 你就明白,這兩個實際是一個東西了。
創(chuàng)建一個新的對象(即實例對象)
把新對象的__proto__ 指向 new 后面構(gòu)造函數(shù) 的原型對象。
把構(gòu)造函數(shù)中的 this 指向 實例對象。
做一些處理,然后返回該實例對象。
下面,手寫一個方法 實現(xiàn)new 的功能:
function New(func){ //func 指傳進來的構(gòu)造函數(shù),關(guān)于構(gòu)造函數(shù),我個人理解來就是用來生成一個對象實例的函數(shù)。 var obj = {}; //這里就直接新建一個空對象了,不用new Object了,效果一樣的,因為我感覺這里講實現(xiàn)new的功能 再用 new 就不太好。 if(func.prototype != null){ obj.__proto__ = func.prototype; } func.apply(obj,Array.prototype.slice.call(arguments, 1)); //把func構(gòu)造函數(shù)里的this 指向obj對象,把傳進來的第二個參數(shù)開始,放入func構(gòu)造函數(shù)里執(zhí)行,比如:屬性賦值。 return obj; }
不知各位看客是否看明白了呢,簡單指明一下把
function create(proto){ var f = function(){}; f.prototype = proto; return new f(); //就等于 function create(proto){ var f = function(){}; var obj = {}; f.prototype = proto; obj.__proto__ = f.prototype; return obj; } //就等于 function create(proto){ var obj = {}; obj.__proto__ = proto; return obj; } //看明白了嗎, 就是用 function f 做了一下過渡。 //驗證 function a(){}; var b = create(a); console.log(b.__proto__ === a); //true;原型鏈開始!
我想,一說到JavaScript的原型是令人奔潰的,其中prototype容易和__proto__兩者的聯(lián)系就太頭疼了,反正看圖比看字舒服。
我看大多數(shù)的教程都是把prototype 和 __proto__放一起講,我覺得還是分開將比較好,本來就不是一個東西
這是個函數(shù)才有的屬性,它保存著對其原型對像的引用,即指向原型對象。(任何函數(shù)都有原型對象。)它的原型對象是看不見,摸不著的,只能通過函數(shù)的prototype屬性來獲取它。
比如
function A(){}; A.prototype; //這樣就獲取到了A的原型對象
總結(jié):你看到prototype,你就想著它對應(yīng)著,它的原型對象就行。
二、原型對象每當(dāng)你創(chuàng)建一個函數(shù),js 就會 對應(yīng)的生成一個原型對象。它只能被函數(shù)的prototype屬性獲取到。(A.prototype 整體變現(xiàn)為A的原型對象)
通過上面知識,我們知道了,對象無非就兩種,實例對象(new 和 字面量創(chuàng)建的對象),和 原型對象。 三、_ proto _屬性這個是非標(biāo)準(zhǔn)的屬性,現(xiàn)代部分瀏覽器支持,比如,火狐,谷歌。對應(yīng)的標(biāo)準(zhǔn)屬性是[[prototype]],這是個隱藏屬性,獲取不到。
這個屬性就是把所有的函數(shù)啊,對象啊,連成一條鏈的東西,我們稱為原型鏈。這條鏈的終點是Object.prototype.__proto__ === null。
那它到底指向誰呢,我給了兩種記憶方式吧:
1、函數(shù)的__proto__ 指向Function.protoype,原型對象的__proto__指向Object.prototype的。字面量 和 new出來的實例對象 ,指向其構(gòu)造函數(shù)(誰 new 出來的)的prototype, Object.create 創(chuàng)建的對象呢,就是上面說的,你給它誰,它就指向誰。
2、除了Object.create創(chuàng)建的對象的__proto__指向你給定的,原型對象的__proto__指向Object.prototype,其他的__proto__ 都是指向其構(gòu)造函數(shù)的原型對象。(你要是看懂了上面的new的實現(xiàn),就應(yīng)該明白為啥了。 )
第二點注意:所有函數(shù)都是 構(gòu)造函數(shù) Function 創(chuàng)建出來的,所以,函數(shù)的構(gòu)造函數(shù) 就是 Function。
選這兩個中你喜歡的一個,對著下面的圖找答案:
constructor 屬性是原型對象獨有的,它指向的就是它的構(gòu)造函數(shù)。上面的一、prototype中說,函數(shù)的prototype屬性指向它的原型對象。此時的函數(shù)是構(gòu)造函數(shù)了。所以函數(shù) 和 它的原型對像 以這兩個 屬性 保持著 相互聯(lián)系。
比如:
function A(){}; A.prototype.constructor === A //true Object.prototype.constructor === Object //true
那函數(shù)的constructor 和 普通對象的 constructor 是怎么回事呢?
開頭說的function A(){};A.constructor 不知道大家有沒有點疑惑?不是原型對象才有的嗎?
其實, 他們就是在一條原型鏈上的,也就是說,A 上沒有 constructor 屬性,它就會沿著原型鏈向上查找,到了 Object的原型對象上,就找到了constructor 屬性,于是就可以用 A.constructor 了.
比如:
var Person = function(name) { this.name = name; } Person.prototype.getName = function(){ return this.name; } var p = new Person("666"); console.log(p.getName()); //666
看這里Person 構(gòu)造函數(shù)里是不是沒有g(shù)etName 方法,但是 p 實例怎么可以用呢?
因為p實例 的__proto__指向 的原型對象上有 getName 函數(shù),所以 p 向原型鏈上查找到了 Person 的原型對象上, 它有g(shù)etName 方法, 于是, p 就可以使用這個方法了。
注意:所以,在找構(gòu)造函數(shù)時,需要注意是在它的原型鏈上找,而不是原型對象上:
Object.constructor === Object.__proto__.constructor //true五、構(gòu)造函數(shù)
啥是構(gòu)造函數(shù)?其實每個函數(shù)都是構(gòu)造函數(shù),只是我們一般把 生成實例對象 的函數(shù) 稱為構(gòu)造函數(shù)(通過new ,new 后面的就是構(gòu)造函數(shù)),本質(zhì)是函數(shù)。
比如:
var Person = function(name) { this.name = name; this.getName = function(){ return this.name; } } var p1 = new Person("zdx"); console.log(p1.getName()); //zdx var p2 = new Person("666"); console.log(p2.getName()) //666 console.log(Person.prototype.constructor === Person) ///true //這里的Person 是個函數(shù)對吧,然后外面用 new Person(); //創(chuàng)建了一個Person 的實例,此時,Person 就是一個構(gòu)造函數(shù)了,也稱它為類,我們把類的首字母都大寫。 //因為,這個函數(shù),使用 new 可以構(gòu)造無數(shù)個實例來。
說一下,怕有人不知道,實例就是實例對象,因為一個實例,它本身就是對象。
六、原型鏈開始放大招了,哈哈:
function Person(name) { this.name = name; } var p = new Person("zdx"); console.log(p.__proto__ === Person.prototype); //true //p是實例對象, 它的構(gòu)造函數(shù)是 Person,按照上面的所說的,它是new 出來的,所以指向它構(gòu)造函數(shù)的prototype console.log(Person.prototype.__proto__ === Object.prototype); //true //Person.prototype 是原型對象, 所以指向 Object.prototype. console.log(Person.__proto__ === Function.prototype); //true //Person 是構(gòu)造函數(shù), 它的構(gòu)造函數(shù)是Function, 所以它就指向 Function.prototype console.log(Function.prototype.__proto__ === Object.prototype); //true //Function.prototype 是原型對象, 所以指向 Object.prototype. console.log(Object.prototype.__proto__ === null); //true //這里就是所有原型鏈的終端,原型鏈到這里就沒了。
所以說js 萬物皆對象呢? 所有的函數(shù)啊,對象啊,實例啊,它的原型鏈最終都到了Object.prototype原型對象。
畫成圖就是醬樣子滴:
其實吧,有時候看圖也不一樣好,哈哈,你可以按照我說的規(guī)則,自己不看圖畫一下。
簡單來驗證一下
var a = {}; //等同與 new Object() console.log(a.prototype); //undefined 對象沒有原型對象 console.log(a.__proto__ === Object.prototype); //true var b = function(){}; console.log(b.prototype); //b的原型對像 console.log(b.__proto__ === Function.prototype); var c = []; //等同于 new Array(),構(gòu)造函數(shù)是Array console.log(c.__proto__ === Array.prototype); //true var d = ""; //等同于 new String(),構(gòu)造函數(shù)是 String console.log(d.__proto__ === String.prototype); //true七、原型鏈的作用
其實,原型鏈的根本作用就是為了 屬性 的讀取。
上面簡單說過,當(dāng)在一個 函數(shù) 或者 對象 上 讀取屬性時,它會先查找自身屬性,如果有,就直接返回,如果沒有呢,就會沿著原型鏈向上查找。
舉個簡單的栗子,每個函數(shù)啊,對象啊,都有toString 方法,它是哪來的呢?
球都麻袋!(等等),不是屬性的讀取嗎。toString這個是方法(函數(shù))呀!同學(xué),你很有眼光嘛。事實上,我們把屬性值為函數(shù)的,稱之為 方法。其實呢,你要了解這些方法是怎么回事。
function test(){}; test.toString(); //"function test(){}" console.log(test.hasOwnProperty("toString")); //false console.log(test.__proto__.hasOwnProperty("toString")); //true //這就找到了,這里的hasOwnProperty 方法 是檢查 該屬性是否 是自身的屬性 //而 (函數(shù)的__proto__)test.__proto__ 都指向(等于) Function.prototype console.log(test.__proto__ === Function.prototype); //true console.log(Function.prototype.hasOwnProperty("toString")); //true //看到這里,明白了嗎。函數(shù)的內(nèi)置方法(函數(shù)) 一部分是 Function.prototype 上的屬性; //一部分? 是的,因為,原型鏈的終端 在 Object.prototype ; //所以,在Object.prototype 上添加的屬性,方法, 函數(shù)也是可以使用的; //比如: Object.prototype.say = function() { console.log(5666) }; function test(){}; test.say(); //5666
那么屬性的賦值是怎么一回事呢?
首先函數(shù) 或 對象 會查找 自身屬性, 如果有,就會 覆蓋該屬性 的值, 如果沒有,它就會創(chuàng)建 一個 自身的屬性,總之,賦值是不會對原型鏈進行查找。
function Person(){}; var p = new Person(); //如果你用的同一個窗口運行這個,結(jié)果可能是上面的 5666,因為剛剛更改了該函數(shù),您重新打開個瀏覽器窗口即可。 p.toString(); //"[object Object]" //有疑惑嗎,其實,toString 這個方法, Function.prototype, 和 Object.prototype 都有; Function.prototype.toString = function(){ console.log("我是Function原型對象的"); } Person.toString(); //我是Function原型對象的 p.toString(); //"[object Object]" //Object上的toString 方法并沒有改變。八、實際代碼中 原型鏈 的運用
運用 最常見的就是 繼承了。
function Person(name){ //父類(構(gòu)造函數(shù)) this.name = name; } Person.prototype.getName = function(){ //在父類的原型對象上添加方法 return this.name; } function Son(name,age){ //子類(構(gòu)造函數(shù)) this.name = name; //為了減輕讀者壓力,就不使用 call 了, Person.call(this, name); this.age = age; } Son.prototype = new Person(); //把子類(構(gòu)造函數(shù)) 的原型對象 掛到 原型鏈上去。 Son.prototype.getAge = function(){ return this.age; } var s = new Son("zdx",666); s.getName(); //Son.prototype 上沒有 getName方法,現(xiàn)在能使用了,就完成了 繼承。
需要解讀一下嘛?
Son.prototype = new Person(); //就等于 var obj = {}; obj.__proto__ = Person.prototype; Son.prototype = obj; //這樣,就把Son.prototype 掛到原型鏈上去了。 Son.prototype.__proto__ === Person.prototype //ture
然后Son 的實例對象 上使用方法 時,就沿著鏈查找, Son.prototype 沒有, 上級Person.prototype 上有。ok,繼承了。
簡單寫一個:
var jQuery = function(name) { return new jQuery.fn.init(); } jQuery.fn = jQuery.prototype = { constructor: jQuery, init: function(name) { this.name = name; }, each: function() { console.log("each"); return this; } } jQuery.fn.init.prototype = jQuery.fn;
估計看起來有點困難,沒事,我們簡化一下
function jQuery(name){ return new init(name); } function init(name){ this.name = name; } jQuery.prototype = { constructor: jQuery, each: function(){ console.log("each") } } init.prototype = jQuery.prototype;
看懂了嗎,你使用jQuery(), 就相當(dāng)于與 new jQuery(); 其實 你 new init() 和 new jQuery(); 是一樣的;因為 init.prototype = jQuery.prototype ,所以它們的實例對象是一樣的。它這樣寫,就是為了你使用方便,不需要你使用 new 來創(chuàng)建jq對象。
使用起來就是這樣的:
$(); //是不是比 new $(); //方便多了。
最后要說的就是,別把原型鏈和作用域鏈搞混了!!!哈哈哈
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/94968.html
摘要:情況沒有明確作用對象的情況下,通常為全局對象例如函數(shù)的回調(diào)函數(shù),它的就是全局對象。正因如此,機器可以作為這類對象的標(biāo)志,即面向?qū)ο笳Z言中類的概念。所以機器又被稱為構(gòu)造函數(shù)。原型鏈也就是繼承鏈。 JS面向?qū)ο蠖?this/原型鏈/new原理 阮一峰JavaScript教程:面向?qū)ο缶幊?阮一峰JavaScript教程:實例對象與 new 命令 阮一峰JavaScript教程:this 關(guān)...
摘要:除了以上介紹的幾種對象創(chuàng)建方式,此外還有寄生構(gòu)造函數(shù)模式穩(wěn)妥構(gòu)造函數(shù)模式。 showImg(https://segmentfault.com/img/remote/1460000018196128); 面向?qū)ο?是以 對象 為中心的編程思想,它的思維方式是構(gòu)造。 面向?qū)ο?編程的三大特點:封裝、繼承、多態(tài): 封裝:屬性方法的抽象 繼承:一個類繼承(復(fù)制)另一個類的屬性/方法 多態(tài):方...
摘要:每一個由構(gòu)造函數(shù)創(chuàng)建的對象都會默認的連接到該神秘對象上。在構(gòu)造方法中也具有類似的功能,因此也稱其為類實例與對象實例一般是指某一個構(gòu)造函數(shù)創(chuàng)建出來的對象,我們稱為構(gòu)造函數(shù)的實例實例就是對象。表示該原型是與什么構(gòu)造函數(shù)聯(lián)系起來的。 本文您將看到以下內(nèi)容: 傳統(tǒng)構(gòu)造函數(shù)的問題 一些相關(guān)概念 認識原型 構(gòu)造、原型、實例三角結(jié)構(gòu)圖 對象的原型鏈 函數(shù)的構(gòu)造函數(shù)Function 一句話說明什么...
摘要:一作用域域表示的就是范圍,即作用域,就是一個名字在什么地方可以使用,什么時候不能使用。概括的說作用域就是一套設(shè)計良好的規(guī)則來存儲變量,并且之后可以方便地找到這些變量。 一、作用域 域表示的就是范圍,即作用域,就是一個名字在什么地方可以使用,什么時候不能使用。想了解更多關(guān)于作用域的問題推薦閱讀《你不知道的JavaScript上卷》第一章(或第一部分),從編譯原理的角度說明什么是作用域。概...
摘要:為了更好的理解,在閱讀此文之前建議先閱讀上一篇進擊之詞法作用域與作用域鏈?zhǔn)裁词情]包閉包的含義就是閉合,包起來,簡單的來說,就是一個具有封閉功能與包裹功能的結(jié)構(gòu)。在中函數(shù)構(gòu)成閉包。 為了更好的理解,在閱讀此文之前建議先閱讀上一篇《進擊JavaScript之詞法作用域與作用域鏈》 1.什么是閉包 閉包的含義就是閉合,包起來,簡單的來說,就是一個具有封閉功能與包裹功能的結(jié)構(gòu)。所謂的閉包就是...
閱讀 3233·2021-09-07 10:10
閱讀 3579·2019-08-30 15:44
閱讀 2578·2019-08-30 15:44
閱讀 2982·2019-08-29 15:11
閱讀 2219·2019-08-28 18:26
閱讀 2745·2019-08-26 12:21
閱讀 1113·2019-08-23 16:12
閱讀 3010·2019-08-23 14:57