摘要:前言文章主要基于高級程序設計總結的基本重寫了全文補充知識點新增實例優(yōu)化排版新增檢測方法技巧用法構造函數創(chuàng)建一個用護定義的對象類型的實例或具有構造函數的內置對象類型之一命令執(zhí)行構造函數返回一個實例對象構造函數一個指定對象實例的類型的函數傳慘一
前言
文章主要基于<< Javascrpt 高級程序設計3 >>總結的!!!
PS: 2018/05/09 基本重寫了全文,補充知識點,新增實例,優(yōu)化排版
PS: 2018/05/11 新增檢測方法,技巧用法
new constructor(arguments):
創(chuàng)建一個用護定義的對象類型的實例或具有構造函數的內置對象類型之一
new命令: 執(zhí)行構造函數返回一個實例對象
構造函數(constructor): 一個指定對象實例的類型的函數
傳慘(arguments): 一個用來被構造函數調用的慘數列表
注意點幾個:
默認構造函數首字母大寫區(qū)分函數類型
在不傳遞任何慘數的情況new constructor可以省略括號
構造函數與其他函數的唯一區(qū)別: 就在于調用它們的方式不同.(任何函數,只要通過 new 操作符來調用,那它就可以作為構造函數;而任何函數,如果不通過 new 操作符來調用,那它跟普通函數也不會有什麼兩樣)
使用構造函數的主要問題,就是每個方法都要在每個實例上重新創(chuàng)建一遍
new存在的意義在于它實現了javascript中的繼承,而不僅僅是實例化了一個對象
這是基本用法,大家都懂的,然后我們往深層次里挖掘下底層原理怎麼運作的.
假設有個函數
function Person(name) { this.name = name || "mike", this.getAge = function () { console.log(10); } } var man = new Person; var women = new Person("tracy") console.log(man.name, women.name) // mike tracy
當代碼執(zhí)行時會經過幾個步驟:
以下是我基于JS高程3總結的:
一個新的空對象被創(chuàng)建
創(chuàng)建執(zhí)行的時候,將構造函數的作用域賦給新對象(因此 this 就指向了這個新對象)
執(zhí)行構造函數中的代碼(為這個新對象添加屬性)等初始化工作
如果構造函數返回了一個“對象”,那麼這個對象會取代整個new出來的結果.如果構造函數沒有返回對象,那麼new出來的結果為步驟1創(chuàng)建的對象,(ps:一般情況下構造函數不返回任何值,如果想覆蓋這個返回值,可以選擇返回一個普通對象.);
以下是原版:
創(chuàng)建一個新對象
將構造函數的作用域賦給新對象(因此 this 就指向了這個新對象)
執(zhí)行構造函數中的代碼(為這個新對象添加屬性)
返回新對象.
下面詳細舉例
1、如果構造函數不返回任何值則按照其他語言一樣返回實例化對象(即步驟1創(chuàng)建的對象).
function Person() { } var man = new Person(); console.log(man) // Person {}
2、如果構造函數返回的是基本類型 (string, number, boolean, null,undefined) 也按照其他語言一樣返回實例化對象(即步驟1創(chuàng)建的對象).如果你們還搞不懂基本類型跟引用對象的區(qū)別,可以參考我之前寫的文章關於javascript基本類型和引用類型小知識
function Person() { return "我是基本類型" } var man = new Person(); console.log(man)//Person {}
3、若返回值是引用類型,則實際返回值為這個引用類型.
function Person() { return { age: 18 } } var man = new Person(); console.log(man) //Object {age: 18}
初學者特別應該注意的是他們之間是不同的,所謂的構造函數是創(chuàng)建一個用戶定義的對象類型的實例或具有構造函數的內置對象類型之一
正常來說構造函數不需要有返回值的,可以認為構造函數和普通函數的最大差別就是:構造函數中沒有return語句,普通函數可以有return語句;構造函數中會使用this關鍵字定義成員變量和成員方法,普通的函數不會使用this關鍵字定義成員變量和方法.
像第二種情況即使返回基本類型也會被忽略掉,只有選擇返回一個普通對象才會取代整個new出來的結果
每個函數都有一個 prototype 屬性, prototype 就是指向通過調用構造函數而創(chuàng)建的那個對象實例的原型對象,作用是可以讓所有對象實例共享它所包含的屬性和方法.
在JavaScript中一切皆對象,每個對象都是繼承自另一個對象,所以對象都有自己的原型對象(除了null以外).
所以除了在構建函數內部定義屬性方法共享之外,我們還可以在構造函數的原型對象上添加共享的自定義屬性方法.
function Person() { } //原型鏈添加函數 Person.prototype.getAge = function () { console.log(18) } //實例化 var man = new Person(), women = new Person(); man.getAge() // 18 women.getAge() // 18
有一種情況是對象實例自身已經賦有同名屬性方法會覆蓋 prototype 上的屬性方法.這個認知是不準確的,下面的原型鏈會講到這部分.
function Person() { } //原型鏈添加函數 Person.prototype.getAge = function () { console.log(18) } //實例化 var man = new Person(); man.getAge = function () { console.log(81) } man.getAge() // 81
還有一種情況是在構造函數的原型對象上添加共享的自定義屬性方法并且實例化對象之后,再次修改原型對象上的方法.同樣會影響到輸出結果,依然下面的原型鏈會講到這部分.
function Person() { } //原型鏈添加函數 Person.prototype.getAge = function () { console.log(18) } //實例化 var man = new Person(); //修改原型鏈自定義函數 Person.prototype.getAge = function () { console.log(81) } man.getAge() // 81原型鏈
下面引出JS高程三解析片段----------
理解原型對象(關鍵詞 prototype, constructor, __proto__)
無論什麼時候,只要創(chuàng)建了一個新函數,就會根據一組特定的規(guī)則為該函數創(chuàng)建一個 prototype 屬性,這個屬性指向函數的原型對象.在默認情況下,所有原型對象都會自動獲得一個 constructor(構造函數)屬性,這個屬性包含一個指向 prototype 屬性所在函數的指針.
就拿前面的例子來說,Person.prototype.constructor 指向 Person .而通過這個構造函數,我們還可繼續(xù)為原型對象添加其他屬性和方法.創(chuàng)建了自定義的構造函數之后,其原型對象默認只會取得 constructor 屬性;至于其他方法,則都是從 Object 繼承而來的.
當調用構造函數創(chuàng)建一個新實例后,該實例的內部將包含一個指針(內部屬性),指向構造函數的原型對象.
ECMA-262 第 5 版中管這個指針叫 [[Prototype]] .雖然在腳本中沒有標準的方式訪問 [[Prototype]] ,但 Firefox、Safari 和 Chrome 在每個對象上都支持一個屬性__proto__ ;而在其他實現中,這個屬性對腳本則是完全不可見的.不過,要明確的真正重要的一點就是,這個連接存在于實例與構造函數的原型對象之間,而不是存在于實例與構造函數之間.
這里引出兩個關鍵知識點:
所有對象都有屬性__proto__指向該對象的構造函數的原型對象,原型鏈就是靠它形成的
函數對象除了__proto__,還有屬性prototype指向該方法的原型對象,它的作用是:構造函數實例化對象的時候,告訴構造函數新創(chuàng)建的對象的原型是誰;
間單說構造函數、原型和實例的關系:每個構造函數都有一個原型對象,原型對象都包含一個指向構造函數的指針,而實例都包含一個指向原型對象的內部指針.原型鏈可以讓我們做到利用原型讓一個引用類型繼承另一個引用類型的屬性和方法.
Javascript 一切皆對象(普通對象和函數對象).每個函數對象都有一個內部鏈接到另一個對象它的原型 prototype.該原型對象有自己的原型,等等,直到達到一個以null為原型的對象.根據定義,null沒有原型,并且作為這個原型鏈 prototype chain中的最終鏈接.
例如:
function Person() { this.name = "mike" } var man = new Person; console.log(man.__proto__ === Person.prototype); // true //繼續(xù)深入發(fā)掘它的原型 console.log(Person.prototype.__proto__ === Object.prototype); // true //前面說的一切皆對象是這個意思 console.log(Function.prototype.__proto__); // Object {} console.log(Array.prototype.__proto__); // Object {} console.log(Number.prototype.__proto__); // Object {} //繼續(xù)深入發(fā)掘它的最終來源是什麼 console.log(Object.prototype); // Object {} console.log(Object.prototype.__proto__); // null
上面一步一步挖到最初的原型,有個注意的點容易混淆的,再強調一遍prototype是函數對象繼承的原型,__proto__是指向創(chuàng)建它的函數對象的原型對象prototype.只有真的弄清楚關系你才知道下面的區(qū)別
console.log(Function.prototype); // function () {} console.log(Array.prototype); // [Symbol(Symbol.unscopables): Object] console.log(Number.prototype); // Number {[[PrimitiveValue]]: 0} console.log(Object.prototype); // Object {} //下面函數對象都是通過new Function()創(chuàng)建,所以指向必定都是Function.prototype. console.log(Function.__proto__); console.log(Array.__proto__); console.log(Number.__proto__); console.log(Object.__proto__); //細細品味這句 console.log(Function.__proto__ === Function.prototype); // true
訪問一個對象的屬性時,它先在該對象上搜尋,如果該對象沒有就搜尋該對象的原型,如果還沒有就繼續(xù)往該對象的原型的原型搜索,依此層層向上搜索,直到找到一個名字匹配的屬性或到達原型鏈的末尾(即undefined)位置.這就是上面說的實例方法覆蓋原型對象方法認知不準確的解釋了
function Person() { this.name = "mike" } Person.prototype.age = 10; Person.prototype.getAge = function() { console.log(20) }; var man = new Person; console.log(man.age) // 10 man.getAge() //20 console.log(man.sex) // undefined
原型對象除了__proto__之外還有一個constructor屬性,作用是返回對創(chuàng)建此對象的函數的引用.這個比較間單,直接上實例
function Person() { this.name = "mike" } var man = new Person; console.log(man.__proto__.constructor === Person); // true console.log(new Array().constructor); // [Function: Array] console.log(new Number().constructor); // [Function: Number] console.log(new Object().constructor); // [Function: Object] console.log(new Function().constructor); // [Function: Function]
實例化對象man打印結果如下.
man.__proto__.constructor === Person
上面獲取原型對象的方法其實不安全,
依賴瀏覽器環(huán)境暴露出來的訪問屬性,
在實例對象變更原型對象指向的情況會失效
function Person() { this.name = "mike" } var man = new Person; man.prototype = Object; console.log(man.prototype); // [Function: Object]
所以我們一般通過Object.getPrototypeOf方法去獲取.
function Person() { this.name = "mike" } var man = new Person; man.prototype = Object; console.log(man.prototype); // [Function: Object] console.log(Object.getPrototypeOf(man)); // Person {}
如果是想要檢測某個對象是否構造函數的實例,可以使用instanceof
function Person() { this.name = "mike" } var man = new Person; console.log(man instanceof Person); // true技巧用法
還記得上面說的構造函數與其他函數的唯一區(qū)別,就在于調用它們的方式不同.任何函數,只要通過 new 操作符來調用,那它就可以作為構造函數;而任何函數,如果不通過 new 操作符來調用,那它跟普通函數也不會有什麼兩樣;
用個小技巧可以讓你沒有使用new操作符也能實例化構造函數,為了區(qū)別改了點代碼.
function Person(name) { //如果是實例化對象直接賦值 if (this instanceof Person) { this.name = name } else { //否則重新實例化 return new Person(name) } } var man = new Person("mike"), women = Person("kitty"); console.log(man.name, women.name); // mike kitty
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://www.ezyhdfw.cn/yun/106174.html
摘要:變量有兩種不同的數據類型基本類型,引用類型。知識一基本類型值就是簡單的數據段引用類型值保存的是對象的引用,不是實際的對象。 ECMAScirpt 變量有兩種不同的數據類型:基本類型,引用類型。 基本的數據類型有:undefined,boolean,number,string,null. 基本類型的訪問是按值訪問的,就是說你可以操作保存在變量中的實際的值。JavaScript中除了上面的...
摘要:從原型對象指向構造函數畫一條帶箭頭的線。線上標注,表示該原型對象的構造函數等于。但除此之外,若構造函數所指的顯示原型對象存在于的原型鏈上,結果也都會為。執(zhí)行構造函數,并將指針綁定到新創(chuàng)建的對象上。 做前端開發(fā)有段時間了,遇到過很多坎,若是要排出個先后順序,那么JavaScript的原型與對象絕對逃不出TOP3。 如果說前端是海,JavaScript就是海里的水 一直以來都想寫篇文章梳理...
摘要:對象詳解對象深度剖析,深度理解對象這算是醞釀很久的一篇文章了。用空構造函數設置類名每個對象都共享相同屬性每個對象共享一個方法版本,省內存。 js對象詳解(JavaScript對象深度剖析,深度理解js對象) 這算是醞釀很久的一篇文章了。 JavaScript作為一個基于對象(沒有類的概念)的語言,從入門到精通到放棄一直會被對象這個問題圍繞。 平時發(fā)的文章基本都是開發(fā)中遇到的問題和對...
閱讀 2538·2021-11-19 09:40
閱讀 3674·2021-11-17 17:08
閱讀 3951·2021-09-10 10:50
閱讀 2305·2019-08-27 10:56
閱讀 2007·2019-08-27 10:55
閱讀 2708·2019-08-26 12:14
閱讀 1049·2019-08-26 11:58
閱讀 1546·2019-08-26 10:43