摘要:實例中的指針僅指向原型,而不指向構(gòu)造函數(shù)。調(diào)用構(gòu)造函數(shù)時會為實例添加一個指向最初原型的或者而把原型修改為另外一個對象就等于切斷了構(gòu)造函數(shù)與最初原型之間的聯(lián)系。
面向?qū)ο蟮某绦蛟O(shè)計
ECMA-262定義對象:無序?qū)傩缘募?,其屬性可以包含基本值,對象或者函?shù)。
普通理解:對象是一組沒有特定順序的值。
對象的每個屬性或方法都有一個名字,而每個名字都映射一個值。
每個對象都是基于一個引用類型創(chuàng)建的。
理解對象屬性類型
在定義只有內(nèi)部的特征(attribute)時,描述了屬性(property)的各種特征。
是為了實現(xiàn)JavaScript引擎用的,在JavaScript中不能直接訪問他們。
為了表示特性是內(nèi)部值,把它們放在了兩對方括號中. 例如: [[Enumerable]]
ECMAScript中有兩種屬性:數(shù)據(jù)屬性和訪問器屬性。
數(shù)據(jù)屬性
數(shù)據(jù)屬性包含一個數(shù)據(jù)值的位置。在這個位置可以讀取和寫入值。數(shù)據(jù)屬性有4個描述行為的特性
[[Configurable]]: 能否通過delete刪除屬性從而重新定義屬性,能夠修改屬性的特性,或者能否把屬性修改為訪問屬性。 默認(rèn)值:false;(不可以重新定義或刪除)
[[Enmuerable]]: 能夠通過for-in循環(huán)返回屬性。(是否可以被枚舉).默認(rèn)值:false;(不可以被枚舉)
[[Writable]]: 能否修改屬性的值。默認(rèn)值:false,(不可以被修改)
[[Value]]:包含這個屬性的數(shù)據(jù)值,讀取屬性的時候,從這個位置讀;寫入屬性值的時候,把新值保存在這個位置。默認(rèn)值:undefiend.
var Person = { name: "Nicholas" } // 創(chuàng)建name屬性。為它置頂?shù)闹凳恰癗icholas”。也就是[[Value]]被設(shè)置了"Nicholas",而對這個值的任何修改都將反映在這個位置。
Object.defineOProperty();
作用:修改屬性默認(rèn)特性
參數(shù)1:屬性所在的對象
參數(shù)2:屬性的名字
參數(shù)3:一個描述符對象
描述符(descriptor)對象的屬性必須是:configurable,enumerable,writable,value.
設(shè)置其中一個或多個值,可以修改對應(yīng)的特性值。
var Person = {}; Object.defineProperty(Person, "name", { writable: false, value: "Nicholas" }); alert(Person.name); //"Nicholas" Person.name = "Greg"; // 設(shè)置成只讀的,屬性是不可修改的。 alert(Person.name); //"Nicholas"
constructor屬性,是無法被枚舉的. 正常的for-in循環(huán)是無法枚舉. [eable = false];
Object.getOwnPropertyNames(); //枚舉對象所有的屬性:不管該內(nèi)部屬性能夠被枚舉.
//3個參數(shù), 參數(shù)1:重新設(shè)置構(gòu)造的對象 (給什么對象設(shè)置) 參數(shù)2:設(shè)置什么屬性 參數(shù)3:options配置項 (要怎么去設(shè)置) Object.defineProperty(Person.prototype,"constructor",{ enumerable: false, //是否是 能夠 被枚舉 value: Person //值 構(gòu)造器的 引用 });
訪問器屬性
訪問器屬性不包含數(shù)據(jù)值,包含一堆geter() 和setter(); 函數(shù) (這兩個函數(shù)都不是必須的)
在讀取訪問器屬性時,會調(diào)用getter(); 這個函數(shù)負(fù)責(zé)返回有效值,在寫入訪問器屬性時,會調(diào)用setter()函數(shù)并傳入新值,這個函數(shù)負(fù)責(zé)決定如何處理數(shù)據(jù)。
訪問器屬性的4個特性:
[[Configurable]]: 能夠通過delete刪除屬性從而定義新的屬性,能否修改屬性的特性,或者能否把屬性修改為數(shù)據(jù)屬性。默認(rèn)值:false; (不可重新定義,和刪除屬性)
[[Enmuerable]]:能否通過for-in循環(huán)返回屬性。默認(rèn)值:false;(不可被枚舉)
[[Get]]: 在讀取屬性時調(diào)用的函數(shù)。默認(rèn)值為:undefeind
[[Set]]: 在寫入屬性時調(diào)用的函數(shù)。默認(rèn)值為:undefiend
訪問器屬性不能直接定義,必須使用Object.defineProperty();來定義。
var book = { _year: 2016, edition: 1 }; Object.defineProperty(book, "year", { get: function(){ return this._year; }, set: function(newValue){ if (newValue > 2016) { this._year = newValue; this.edition += newValue - 2016; } } }); book.year = 2020; alert(book.edition); // 5 // _year前面的下劃線是一種常用的記號,用于表示只能通過對象方法訪問的屬性。
定義多個屬性
Object.defineProperties();
定義多個屬性。
參數(shù)1:對象要添加和修改其屬性的對象
參數(shù)2:對象的屬性與第一個對象中要添加或修改的屬性一一對應(yīng).
var book = {}; Object.defineProperties(book, { _year: { value: 2016 }, edition: { value: 1 }, year: { get: function () { return this._year; }, set: function (newValue) { this._year = newValue; } } });
讀取屬性的特性
Object.getOwnPropertyDescriptor()
取得給定屬性的描述符.
參數(shù)1:屬性所在的對象
參數(shù)2:要讀取其描述符的屬性名稱
返回值:對象。如果是訪問器屬性,這個對象含有:configurable,enumerable,get和set
如果是數(shù)據(jù)屬性,這個對象含有:configureable,enumerbale,writeable,value
工廠模式
解決:創(chuàng)建多個相似對象。(未解決:對象識別的問題-怎么知道一個對象的類型)
function createPerson(name, age, job){ var o = new Object(); o.name = name; o.age = age; o.job = job; o.sayName = function(){ alert(this.name); }; return o; } var person1 = createPerson("Nicholas", 29, "Software Engineer"); var person2 = createPerson("Greg", 27, "Doctor");
構(gòu)造函數(shù)模式
function Person(name, age, job){ this.name = name; this.age = age; this.job = job; this.sayName = function(){ alert(this.name); }; } var person1 = new Person("Nicholas", 29, "Software Engineer"); var person2 = new Person("Greg", 27, "Doctor");
沒有顯示的創(chuàng)建對象
直接將屬性和方法賦給this對象
沒有reutrn語句
創(chuàng)建實例會經(jīng)過:
創(chuàng)建一個對象
將構(gòu)造函數(shù)的作用域賦給新對象(因此this就指向了這個新對象)
執(zhí)行構(gòu)造函數(shù)中的代碼(為這個新對象添加屬性)
返回新對象
對象的 constructor 屬性最初是用來表示對象類型。
提到檢測對象類型,還是使用instanceof操作符要更可靠。
將構(gòu)造函數(shù)當(dāng)作函數(shù)
構(gòu)造函數(shù)與其它函數(shù)的唯一區(qū)別,在于調(diào)用它們的方式不同。
構(gòu)造函數(shù)也是函數(shù),不存在定義構(gòu)造函數(shù)的語法。任何函數(shù),只要通過new操作來調(diào)用,就可以作為構(gòu)造函數(shù)。
任何函數(shù),如果不通過new操作符來調(diào)用,就跟普通函數(shù)沒什么不同。
構(gòu)造函數(shù)的問題
每個方法都要在每個實例上重新創(chuàng)建
通過原型模式來解決
原型模式
每個函數(shù)都有一個 prototype (原型)屬性,這個屬性是一個指針,指向一個對象,
而這個對象的用途是包含可以由特定類型的所有實例共享的屬性和方法
每個函數(shù)都有一個prototype(原型)屬性,這個屬性石一個指針,指向一個對象,而這個對象的用途是包含可以由特定類型的所有實例共享的屬性和方法。
字面的理解:prototype就是通過調(diào)用構(gòu)造函數(shù)而創(chuàng)建的那個對象實例的原型。
讓所有對象實例共享它所有包含的屬性和方法。不必在構(gòu)造函數(shù)中定義對象實例的信息,而是將這些信息直接添加到原型中。
理解原型對象
只要創(chuàng)建了一個函數(shù),就會根據(jù)一組特定的規(guī)則為該函數(shù)創(chuàng)建一個prototype屬性,這個屬性指向函數(shù)的原型對象。
默認(rèn)情況下:所有原型對象都會自動獲得一個constructor(構(gòu)造函數(shù))屬性,這個屬性包含一個指向prototype屬性所在函數(shù)的指針。
創(chuàng)建了自定義的構(gòu)造函數(shù)之后,其原型對象默認(rèn)只會取得constructor屬性。
當(dāng)調(diào)用構(gòu)造函數(shù)創(chuàng)建一個新實例后,該實例的內(nèi)部將包含一個指針(內(nèi)部屬性),指向構(gòu)造函數(shù)的原型對象。
原型鏈查找:
每當(dāng)代碼讀取某個對象的某個屬性時,都會執(zhí)行一次搜索,目標(biāo)是具有給定名字的屬性。搜索首先從對象實例本身開始。如果在實例中找到了具有給定名字的屬性,則返回該屬性的值,如果沒有找到,則繼續(xù)搜索指針指向的原型對象,在原型對象中查找具有給定名字的屬性。如果在原型對戲那個中找到這屬性,則返回該屬性的值。
可以通過對象實例訪問保存在原型中的值,但卻不能通過對象實例重寫原型中的值,如果在實例中添加了一個屬性,而該屬性與實例原型中的一個屬性同名,那在實例中創(chuàng)建該屬性,該屬性將會屏蔽原型中的那個屬性。
function Person(){ } Person.prototype.name = "Nicholas"; Person.prototype.age = 29; Person.prototype.job = "Software Engineer"; Person.prototype.sayName = function(){ alert(this.name); }; var person1 = new Person(); var person2 = new Person(); person1.name = "Greg"; alert(person1.name); //"Greg" —— 來自實例的name alert(person2.name); //"Nicholas" -- 來自原型中的name
當(dāng)為對象實例添加一個屬性時,這個屬性就會屏蔽原型對象中保存的同名屬性。添加這個屬性只會組織訪問原型中的那個屬性,但不會修改那個屬性。即使將這個屬性設(shè)置為null,也只會在實例中設(shè)置這個屬性,而不會恢復(fù)其指向原型的連接??梢允褂胐elete操作符可以完全刪除實例屬性。
hasOwnProperty() // 從Object繼承而來的方法
檢測一個屬性是否存在于實例中,還是存在于原型。
判斷一個對象屬性 是屬于 原型屬性 還是屬于 實例屬性
在實例中,返回true
在原型上,返回false
function Person() {} Person.prototype.name = "Nicholas"; var p1 = new Person(); console.log(p1.hasOwnProperty("name")); // false
原型與in操作符
in 操作符,通過對象能夠訪問給定屬性時返回true,無論是在實例中還是原型中。能夠訪問到,就返回true.
同時使用hasOwnProperty(); 和 in操作符 : 確定是屬性存在實例中,還是原型中。
// 判斷屬于原型上的屬性 function hasPrototypeProperty( obj, attr ) { return !obj.hasOwnProperty(attr) && attr in obj; }
Object.keys()
參數(shù):接收一個對象作為參數(shù),返回一個包含所有可枚舉的屬性的字符串?dāng)?shù)組。
function Person() {} Person.prototype.name = "Nicholas"; Person.prototype.age = 20; var p1 = new Person(); var keys = Object.keys(Person.prototype); console.log(keys); // ["name", "age"]
更簡單的原型方法
function Person() {} Person.prototype = {}
將Person.prototype設(shè)置為等于一個對象字面量形式創(chuàng)建的新對象。
會造成constructro屬性不再指向Person。
每創(chuàng)建一個函數(shù),就會同時創(chuàng)建它的peorotype,這個對象也會自動獲得constructro屬性。直接賦值為一個字面量形式,本質(zhì)上是完全重寫了默認(rèn)的prottoype對象,因此constructor屬性也就變成新的對象的constructor屬性(指向Object構(gòu)造函數(shù)) 不再指向Person函數(shù)。
通過instanceof操作符能夠返回正確的結(jié)果,但是通過constructor已經(jīng)無法確定對象的類型了。
可以手動設(shè)置回constructor屬性為當(dāng)前的類
function Person () {} Person.prototype = { constructor: Person, name: "Nicholas" }
這種方式重設(shè)constructor屬性會導(dǎo)致它的[[Enumerable]]特性被設(shè)置為true
constructor是不可被枚舉,可以通過Object.definePropert();修改特性,重置構(gòu)造函數(shù)。
Object.defineProperty(Person.protype, "constructor", function () { enmuerable: false, value: Person });
原型動態(tài)
由于在原型中查找值的過程是一次搜索,對原型對象所做的任何修改都能夠立即從實例上反應(yīng)出來,即使是先創(chuàng)建了實例后修改原型也是一樣。
實例中的指針僅指向原型,而不指向構(gòu)造函數(shù)。
可以隨時為原型添加屬性和方法,并且修改能夠立即在所有對象實例中反映出來,但如果是重寫了這個原型對象,那么就不一樣。
調(diào)用構(gòu)造函數(shù)時會為實例添加一個指向最初原型的[[Prototype]] 或者_(dá)_proto__ .而把原型修改為另外一個對象就等于切斷了構(gòu)造函數(shù)與最初原型之間的聯(lián)系。
function Person () {} var firend = new Person(); Person.prototype = { constructor: Person, name: "Nicholas", sayName: function () { console.log(this.name); } } firend.sayName(); // error // friend 指向的原型中不包含以該名字命名的屬性
原生對象的原型
原型模式的體現(xiàn)在創(chuàng)建自定義類型方面,所有的原生的引用類型,都是采用這種模式創(chuàng)建的。
所有元素引用類型(Object,Array,String...)都在其構(gòu)造函數(shù)上定義了方法
通過原生對象的原型,不僅可以取得所有默認(rèn)方法的引用,而且也可以定義新的方法。
console.log( typeof Array.prototype.slice ); // function
不建議這樣使用,會導(dǎo)致命名沖突,也可能意外的重寫原生的方法。
原型對象的問題
原型模式省略了為構(gòu)造函數(shù)傳遞參數(shù)初始化的,造成所有實例默認(rèn)情況下都將取得相同的屬性值。
原型模式最大的問題是由其共享的本性所導(dǎo)致。
對于包含引用類型的原型對象,如果修改其值,那么另個實例也會修改。
function Person () {} Person.prototype = { constructor: Person, name: "Nicholas", friends: ["Shelby", "Court"] } var p1 = new Person(); var p2 = new Person(); p1.friends.push("Van"); console.log(p1.friends); // ["Shelby", "Court", "Van"] console.log(p1.friends); // ["Shelby", "Court", "Van"]
實例一般都要有屬性自己的全部屬性。這個問題造成很少會多帶帶使用原型模式。
組合使用構(gòu)造函數(shù)模式和原型模式
構(gòu)造函數(shù)模式:用于定義實例的屬性
原型模式:用于定義方法和共享的屬性。
結(jié)果:每個實例都會有自己的一份實例屬性的副本,但同時又共享這對方法的引用,最大限度的節(jié)省內(nèi)存,同時還支持向構(gòu)造函數(shù)傳遞參數(shù)。
是目前在ECMAScript中使用最廣泛,認(rèn)同度最高的一種創(chuàng)建自定義類型的方法。是用來定義引用類型的一種默認(rèn)模式。
動態(tài)原型模式
把信息都封裝到函數(shù)中,這樣體現(xiàn)了封裝的概念。
通過在構(gòu)造函數(shù)中初始化原型(僅在必要情況下),同時保持了同時使用構(gòu)造函數(shù)和原型的優(yōu)點。
可以通過檢查默認(rèn)應(yīng)該存在的方法是否有效,來決定是否需要初始化運原型。
//動態(tài)原型模式:(讓你的代碼 都封裝到一起) function Person( name,age,firends ) { this.name = name; this.age = age; this.firends = firends; //動態(tài)原型方法 if ( typeof this.sayName !== "function" ) { Person.prototype.sayName = function () { console.log(this.name); } } }
sayName()方法不存在的情況下,才會將它添加到原型中if語句中的代碼只會在初次調(diào)用構(gòu)造函數(shù)時才會執(zhí)行。此后,原型已經(jīng)完成初始化,不需要再做什么修改。這邊的原型所做的修改,能夠理解在所有實例中得到反映。
if語句的檢查可以是初始化之后應(yīng)該存在的任何屬性或方法---不必用一大堆if語句檢查每個屬性和每個方法。只要檢查其中一個即可。
采這種模式創(chuàng)建的對象,還可以使用instanceof操作符確定它的類型。
使用動態(tài)原型模式時,不能使用對象字面量重寫原型。如果在已經(jīng)創(chuàng)建了實例的情況下重寫原型,就會切斷現(xiàn)有實例與新原型之間的聯(lián)系。
寄生構(gòu)函數(shù)模式
創(chuàng)建一個函數(shù),該函數(shù)的作用僅僅是封裝創(chuàng)建對象的代碼,然后再返回新創(chuàng)建的對象。
在工廠模式,組合使用構(gòu)造函數(shù)模式和原型模式,都不能使用的情況下,使用。
這個模式與工廠模式是類似的,構(gòu)造函數(shù)在不返回值的情況下,默認(rèn)會返回新對象的實例。而在構(gòu)造函數(shù)末尾添加一個return語句,可以重寫調(diào)用構(gòu)造函數(shù)時返回出來的值。
function Person ( name ) { var o = new Object(); o.name = name; o.sayName = function () { console.log(this.name); } return o; } var friend = new Person("Van"); friend.sayName();
寄生構(gòu)造函數(shù)模式,首先,返回的對象與構(gòu)造函數(shù)或者與構(gòu)造函數(shù)的原型屬性之間沒有關(guān)系。
構(gòu)造函數(shù)返回的對象與在構(gòu)造函數(shù)外部創(chuàng)建的對象沒有什么不同,為此,不恩呢該依賴 instanceof操作符來確定對象類型。
一般來說,不可通過prototype修改原生對象的方法??梢允褂眉纳鷺?gòu)造函數(shù)模式,達(dá)到特殊需求。
穩(wěn)妥構(gòu)造函數(shù)模式
穩(wěn)妥模式就是沒有公共屬性,而且其他方法也不引用this對象,穩(wěn)妥模式最適合在安全的環(huán)境中使用。如果程序?qū)τ诎踩砸蠛芨?,那么非常適合這種模式。
也不能使用new關(guān)鍵字。
//穩(wěn)妥構(gòu)造函數(shù)式 durable object (穩(wěn)妥對象) //1,沒有公共的屬性 //2,不能使用this對象 function Person ( name,age ) { //創(chuàng)建一個要返回的對象。 利用工廠模式思維。 var obj = new Object(); //可以定義一下是有的變量和函數(shù) private var name = name || "zf"; // var sex = "女"; // var sayName = function () { // } //添加一個對外的方法 obj.sayName = function () { console.log(name); } return obj; } var p1 = Person("xixi",20); p1.sayName();繼承
許多OO語言橫縱都支持兩種繼承方式:接口繼承 和實現(xiàn)繼承。
接口繼承:只繼承方法簽名
實現(xiàn)繼承:繼承實際的方法。
由于函數(shù)中沒有簽名,在ECMAScript中無法實現(xiàn)接口繼承,ECAMScript 只支持實現(xiàn)繼承,而其實現(xiàn)繼承主要原型鏈來實現(xiàn)。
原型鏈
基本思想:讓一個引用類型繼承另一個引用類型的屬性和方法。
讓原型對象等于另一個類型的實例,此時的原型對象將包含一個指向另一個原型的指針,相應(yīng)的,另一個原型中也包含指向另一個構(gòu)造函數(shù)的指針。
實現(xiàn)的本質(zhì)是重寫原型對象,代之以一個新類型的實例
function SuperType () { this.property = true; } SuperType.prototype.getSuperValue = function () { return this.property; } function SubType () { this.subproperty = false; } // 繼承了SuperType SubType.prototype = new SuperType(); SubType.prototype.getSubValue = function () { return this.subproperty; } var instance = new SubType(); console.log( instance.getSuperValue() ); // true
調(diào)用 instance.getSuperValue()會經(jīng)歷三個步驟:
1:搜索實例
2:搜索SubType.prototype
3:搜索SuperTpe.prototype
別忘記默認(rèn)的原型
所有函數(shù)的原型都是Object的實例,因此默認(rèn)原型都會包含一個內(nèi)部指針,指向Object.prototype確定原型和實例的關(guān)系
可以通過兩種方式來確定原型和實例之間的關(guān)系。第一種方式是使用 instanceof 操作符,只要用
這個操作符來測試實例與原型鏈中出現(xiàn)過的構(gòu)造函數(shù),結(jié)果就會返回 true 。
方式1:使用instanceof操作符。 測試實例與原型鏈中出現(xiàn)的構(gòu)造函數(shù),就會返回true。
alert(instance instanceof Object); //true alert(instance instanceof SuperType); //true alert(instance instanceof SubType); //true
方式2:使用isPrototypeOf(); 是原型鏈中出現(xiàn)過的原型,都可以說是該原型鏈所派生的實例的原型。就會返回true。
alert(Object.prototype.isPrototypeOf(instance)); //true alert(SuperType.prototype.isPrototypeOf(instance)); //true alert(SubType.prototype.isPrototypeOf(instance)); //true
謹(jǐn)慎地定義方法
子類型有時候需要重寫超類型中的某個方法,或者需要添加超類型中不存在的某個方法。
給原型添加方法的代碼一定要放在替換原型的語句之后。
即在通過原型鏈實現(xiàn)繼承時,不能使用對象字面量創(chuàng)建原型方法。因為這
樣做就會重寫原型鏈,
即在通過原型鏈實現(xiàn)繼承時,不能使用對象字面量創(chuàng)建原型方法。這樣會重寫原型鏈。導(dǎo)致替換原型無效。
原型鏈的問題
原型鏈可以實現(xiàn)繼承,但是存在一些問題:包含引用類型的原型。
通過原型來實現(xiàn)繼承時,原型實際上會變成另一個類型的實例。于是,原先的實例屬性也就變成了現(xiàn)在的原型屬性了。
在構(gòu)造函數(shù)中,而不是原型對象中定義屬性的原因:包含引用類型的原型屬性會被實例所共享。
第二個問題:在創(chuàng)建子類型的實例時,不能向超類型的構(gòu)造函數(shù)中傳遞參數(shù)。
原因:沒有辦法在不影響所有對象的實例情況下,給超類型的構(gòu)造函數(shù)傳遞參數(shù)。
實踐中很少多帶帶使用原型鏈。
借用構(gòu)造函數(shù)
在子類型構(gòu)造函數(shù)的內(nèi)部調(diào)用超類型構(gòu)造函數(shù)
函數(shù)只是在特定環(huán)境中執(zhí)行代碼的對象,因此通過使用apply()和call()方法可以在新創(chuàng)建的對象上執(zhí)行構(gòu)造函數(shù)
傳遞參數(shù)
在類型構(gòu)造函數(shù)中向超類型構(gòu)造函數(shù)傳遞參數(shù)
function SuperType ( name ) { this.name = name; } function SubType ( name ) { // 繼承了SuperType,同時傳遞參數(shù) SuperType.call(this,name) this.age = 21; } var instance = new SubType("cyan"); console.log( instance.name ); // cyan console.log( instance.age ); // 21
借用構(gòu)造函數(shù)的問題
無法避免構(gòu)造函數(shù)模式存在的問題--方法都定義構(gòu)造函數(shù)中定義。
超類型的原型中定義的方法,對子類型而言也是不可見的。
借用構(gòu)造函數(shù)很少多帶帶使用
組合繼承
將原型鏈和借用構(gòu)造函數(shù)的技術(shù)組合到一塊,發(fā)揮二者之長的一種繼承模式。
思路:使用原型鏈的實現(xiàn)對象原型屬性和方法的繼承,通過借用構(gòu)造函數(shù)實現(xiàn)的對象屬性的繼承。
結(jié)果:即通過在原型上定義方法實現(xiàn)了函數(shù)復(fù)用,有能夠保證每個實例都有它自己的屬性。
instanceof 和isPrototypeOf(); 能夠用于識別基于組合創(chuàng)建的對象。 (JavaScript中最常用的繼承模式)
缺點:會執(zhí)行構(gòu)造函數(shù)二次。
原型式繼承
借助原型可以基于已有的對象創(chuàng)建新對象,同時還不比因此創(chuàng)建自定義類型。
function object(o){ function F(){} F.prototype = o; return new F(); }
在 object(); 函數(shù)內(nèi)部,先創(chuàng)建了一個臨時性的構(gòu)造函數(shù),然后將傳入的對象作為這個構(gòu)造函數(shù)的原型,最后返回這個臨時類型的一個新實例。本質(zhì)上:boject()對傳入其中的對象執(zhí)行了一次淺復(fù)制
//2件事: 繼承了1次父類的模板,繼承了一次父類的原型對象 function Person ( name,age ) { this.name = name; this.age = age; } Person.prototype = { constructor: Person, sayHello: function () { console.log("hello world!"); } } function Boy ( name,age,sex ) { //call 綁定父類的模板函數(shù) 實現(xiàn) 借用構(gòu)造函數(shù)繼承 只復(fù)制了父類的模板 // Person.call(this,name,age); Boy.superClass.constructor.call(this,name,age); this.sex = sex; } //原型繼承的方式: 即繼承了父類的模板,又繼承了父類的原型對象。 // Boy.prototype = new Person(); //只繼承 父類的原型對象 extend(Boy,Person); // 目的 只繼承 父類的原型對象 , 需要那兩個類產(chǎn)生關(guān)聯(lián)關(guān)系. //給子類加了一個原型對象的方法。 Boy.prototype.sayHello = function () { console.log("hi,js"); } var b = new Boy("zf",20,"男"); console.log( b.name ); console.log( b.sex ); b.sayHello(); Boy.superClass.sayHello.call(b); //extend方法 //sub子類, sup 父類 function extend ( sub,sup ) { //目的, 實現(xiàn)只繼承 父類的原型對象。 從原型對象入手 //1,創(chuàng)建一個空函數(shù), 目的:空函數(shù)進(jìn)行中轉(zhuǎn) var F = new Function(); // 用一個空函數(shù)進(jìn)行中轉(zhuǎn)。 // 把父類的模板屏蔽掉, 父類的原型取到。 F.prototype = sup.prototype; // 2實現(xiàn)空函數(shù)的原型對象 和 超類的原型對象轉(zhuǎn)換 sub.prototype = new F(); // 3原型繼承 //做善后處理。 還原構(gòu)造器 , sub.prototype.constructor = sub; //4 ,還原子類的構(gòu)造器 //保存一下父類的原型對象 // 因為 ①方便解耦, 減低耦合性 ② 可以方便獲得父類的原型對象 sub.superClass = sup.prototype; //5 ,保存父類的原型對象。 //自定義一個子類的靜態(tài)屬性 , 接受父類的原型對象。 //判斷父類的原型對象的構(gòu)造器, (防止簡單原型中給更改為 Object) if ( sup.prototype.constructor == Object.prototype.constructor ) { sup.prototype.constructor = sup; //還原父類原型對象的構(gòu)造器 } }
Object.create()
參數(shù)1:用作新對象原型的對象。
參數(shù)2(可選)一個為新對象額外屬性的對象. 配置參數(shù)(每個屬性都是通過高自己的描述符定義)
傳入一個參數(shù)的情況下:Object.create(); 與 object() 方法的行為相同.
注意:定義任何屬性都會覆蓋原型對象上同名屬性。
包含引用類型值的屬性始終都會共享相應(yīng)的值
var person = { name: "cyan", firends: ["tan", "kihaki", "van"] } var anotherPerson = Object.create(person, { name: { value: "red" } }); console.log( anotherPerson.name ); // red
寄生式繼承
創(chuàng)建一個僅用于封裝過程的函數(shù),該函數(shù)在內(nèi)部以某種方式來增強對象,最后返回對象。
function createAnother ( original ) { var cloen = Object.create(original); // 調(diào)用函數(shù)創(chuàng)建一個新對象 clone.sayHi = function () { // 以某種方式來增強這個對象 console.log("hi"); } return clone; // 返回這個對象 }
寄生組合式繼承
解決,構(gòu)造函數(shù)調(diào)用二次的問題。
通過借用構(gòu)造函數(shù)來繼承屬性,通過原型鏈的混成形式來繼承方法。
基本思路:不必為了指定子類型的原型而調(diào)用超類型構(gòu)造函數(shù)。需要的是超類型的原型的一個副本。
本質(zhì):使用寄生式繼承來繼承超類型的原型,然后再將指定給子類型的原型。
沒有使用new關(guān)鍵字
function inheritProtoype ( subType, superType ) { var prototype = Object.create(superType.prototype); // 創(chuàng)建對象 prototype.constructor = subType; // 增強對象 subType.prototype = prototype; // 指定對象 } // 1: 創(chuàng)建超類型的原型的一個副本。 // 2:為創(chuàng)建的副本添加添加constructor屬性,從而彌補因為重寫原型而市區(qū)去的constructor屬性 // 3: 將新創(chuàng)建的對象,賦值給子類型的原型。函數(shù)表達(dá)式
通過name 可以訪問函數(shù)名(非標(biāo)準(zhǔn)屬性)
關(guān)于函數(shù)聲明,重要特征就是函數(shù)聲明提升:代碼執(zhí)行之前會先讀取函數(shù)聲明。
匿名函數(shù)(也叫拉姆達(dá)函數(shù)): function 關(guān)鍵字后面沒有標(biāo)識符
匿名函數(shù)的name屬性時空字符串
遞歸函數(shù): 一個函數(shù)通過名字調(diào)用自身的情況
function factorial (num) { if ( num <= 1 ) { // 遞歸出口 return 1; } else { return num * arguments.callee(num-1); // 遞歸點 } }
命名函數(shù)表達(dá)式:
var factorial = (function f ( num ) { if ( num <= 1 ) { return 1; } else { return num * f(num-1); } });
創(chuàng)建了一個名為f() 的命名函數(shù)表達(dá)式,然后將它賦值給變量factorial。即使把函數(shù)賦值給另一個變量,函數(shù)的名字f仍然是有效的。
閉包閉包:指有權(quán)訪問另一個函數(shù)中作用域中的變量的函數(shù)。
表現(xiàn):在一個函數(shù)內(nèi)部創(chuàng)建另一個函數(shù)
當(dāng)某個函數(shù)被調(diào)用的時候,會創(chuàng)建一個執(zhí)行環(huán)境(execution context)及相應(yīng)的作用域鏈。然后使用arguments和其它命名參數(shù)的值來初始化函數(shù)的活動對象(activation object)。
在作用域鏈中,外部函數(shù)的活動對象始終處于第二位,外部函數(shù)的外部函數(shù)的活動對象處于第三位直至作用域鏈的終點的全局執(zhí)行環(huán)境。
后臺的每個執(zhí)行環(huán)境都有一個表示變量的對象---變量對象
作用域鏈中至少包含二個變量對象:本地活動對象和全局變量對象。
作用域鏈本質(zhì):一個指向變量對象的指針列表,引用但不實際包含變量對象。
無論什么時候在函數(shù)中訪問一個變量時,就會從作用域鏈中搜索具有相應(yīng)名字的變量。
一般來講,當(dāng)函數(shù)執(zhí)行完畢后,局部活動對象就會被銷毀,內(nèi)容中近保存全局作用域(全局執(zhí)行環(huán)境的比變量對象)。
但是,閉包可以延長變量的生存周期
function createComparisonFunction( propertyName ) { return function ( object1, object2 ) { var val1 = object1[propertyName]; var val2 = object1[propertyName]; if ( val1 < val2 ) { return -1; } else if ( val1 > val2 ) { return 1; } else { return 0; } } } var compare = createComparisonFunction("name"); var reslut = compare({name: "cyan"}, {name: "tan"}); // 告知垃圾回收機制將其清除, // 隨著匿名函數(shù)的作用域鏈被銷毀,其它作用域(除了全局作用域)也都可以安全的銷毀。 compare = null; // 接觸對匿名函數(shù)的引用(以便釋放內(nèi)存)
createComparisonFunction()函數(shù)執(zhí)行完畢后,其活動對象也不會被銷毀,因為匿名函數(shù)的作用域鏈仍然在引用這個活動對象。
當(dāng)createComparisonFunction()函數(shù)執(zhí)行返回后,其執(zhí)行環(huán)境的作用域鏈會被銷毀,但它的活動對象仍會留在內(nèi)存中,直至匿名函數(shù)被銷毀。createComparisonFunction()的活動對象才會被銷毀。
閉包的問題:
由于閉包會攜帶包含它的函數(shù)的作用域,因此會比其他函數(shù)占用過多的內(nèi)存。過度使用閉包可能會導(dǎo)致內(nèi)存占用過多,V8引擎優(yōu)化后,JavaScript引擎會嘗試回收被閉包占用的內(nèi)存。
閉包與變量
閉包只能取得包含函數(shù)中任何變量的最后一值。(循環(huán)嵌套函數(shù)的i問題)
閉包所保存的是整個變量對象,而不是某個特殊的變量。
function createFunctions () { var reslut = []; for ( var i=0; i<10; i++ ) { reslut[i] = function (num) { return function () { return num; } }(i); } return reslut; }
定義一個匿名函數(shù),并將立即執(zhí)行該匿名函數(shù)的結(jié)果賦給數(shù)組。由于函數(shù)參數(shù)是按值傳遞的,所以就會將變量i的當(dāng)前值復(fù)制給參數(shù)num。而在這個匿名函數(shù)內(nèi)部,有創(chuàng)建并返回了一個訪問num的閉包。這樣,reslut數(shù)組中的每個函數(shù)都有自己num變量的一個副本,因此可以返回各自不同的數(shù)值。
關(guān)于this對象
this對象是運行時就函數(shù)執(zhí)行環(huán)境綁定的:在全局函數(shù)中,this等于window,而當(dāng)函數(shù)被作為某個對象的方法調(diào)用時,this等于那個對象。
匿名函數(shù)的執(zhí)行環(huán)境具有全局性,因此其this對象通常指向window。但有時候由于編寫閉包的方式不同,這一點不明顯。(在通過call()或apply()來改變函數(shù)執(zhí)行環(huán)境的情況下,this就會指向其它對象。)
var name = "window"; var object = { name: "object", getNameFunc: function () { return function () { return this.name; } } } console.log( object.getNameFunc()() );
先創(chuàng)建了一個全局變量name,有創(chuàng)建一個包含name屬性的對象。這個對象包含一個方法--getNameFunc(); 它返回一個匿名函數(shù),而匿名函數(shù)又返回this.name。由于getNameFunc();返回一個函數(shù),因此調(diào)用object.getNameFunc()(); 就立即調(diào)用它返回的函數(shù),結(jié)果返回一個字符串。 結(jié)果的name變量的值是全局的。為什么匿名函數(shù)沒有取得其包含作用域(或外部作用域)的this對象呢?
每個函數(shù)在被調(diào)用時都會自動取得兩個特殊變量:this和arguments。內(nèi)部函數(shù)在搜索這兩個變量時,只會搜索到其活動對象為止,因此永遠(yuǎn)不可能直接訪問外部函數(shù)中的這個兩個變量。
可以通過保存this引用來訪問
var name = "window"; var object = { name: "object", getNameFunc: function () { var self = this; return function () { return self.name; } } } console.log( object.getNameFunc()() );
this 和arguments存在同樣的問題,如果想訪問作用域中的arguments對象,必須將對該對象的引用保存到另一個閉包能夠訪問的變量中。
內(nèi)存泄漏
如果閉包的作用域鏈保存著一個HTML元素,就意味著該元素將無法被銷毀
閉包會引用包含函數(shù)的整個活動對象。
包含函數(shù)的活動中也仍然會保存一個引用。因此,有必要把element變量設(shè)置null。這樣能夠接觸對DOM對戲那個的引用。順利地減少其引用數(shù)據(jù),確保正?;厥掌湔加玫膬?nèi)存。
模仿塊級作用域
JavaScript中沒有塊級作用域的概念
function outputNumbers ( count ) { for ( var i=0; i變量i是定義在outputNumbers()的活動對象中,因此從它有定義開始,就可以在函數(shù)內(nèi)部隨處訪問它。
匿名函數(shù)用來模仿塊級作用域并避免這個問題。
塊級作用域:稱為私有作用域
(function () {})();將函數(shù)聲明包含在一堆圓括號中,表示它實際上是一個函數(shù)表達(dá)式,而緊隨其后的另一對圓括號會立即調(diào)用這個函數(shù)。
JavaScript將function 關(guān)鍵字作一個函數(shù)聲明的開始,而函數(shù)后聲明不能跟圓括號。然后函數(shù)表達(dá)式的后面可以跟圓括號。
將函數(shù)聲明轉(zhuǎn)換成表達(dá)式使用()
需求:只要臨時需要一些變量,就可以使用私有化作用域function outputNumbers ( count ) { (function () { for ( var i=0; i在匿名函數(shù)中的定義的任何變量,都會在執(zhí)行結(jié)束時被銷毀。
結(jié)果:減少閉包占用的內(nèi)存問題,因為沒有指向匿名函數(shù)的引用。只要函數(shù)執(zhí)行完畢,就可以立即銷毀其作用域鏈了。
私有變量特權(quán)方法:有權(quán)訪問私有變量和私有方法的公有方法。
作用:封裝性,隱藏那些不應(yīng)該被被直接修改的屬性,方法。
缺點:必須使用構(gòu)造函數(shù)模式來達(dá)到這個目的。
構(gòu)造函數(shù)本身是有缺點:對每個實例都是創(chuàng)建同樣一組新方法。構(gòu)造函數(shù)中定義特權(quán)方法:
function MyObject () { // 私有變量和私有函數(shù) var privateVariable = 10; function prvateFunction () { return false; } // 特權(quán)方法 this.publicMethod = function () { privateVariable++; return prvateFunction(); } }靜態(tài)私有變量
目的:創(chuàng)建靜態(tài)變量會因為使用原型而增進(jìn)代碼的復(fù)用,但沒有實例對象都沒有自己的私有變量。使用閉包和私有變量缺點:多查找作用域中的一個層次,就會在一定程度上影響查找速度。
模塊模式
目的:為單例創(chuàng)建私有變量和特權(quán)方法。
本質(zhì):對象字面量定義的是單例的公共接口單例:只有一個實例的對象。
慣例:JavaScript是以對象字面量的方式來創(chuàng)建單例對象。
作用:對單例進(jìn)行某些初始化,同時又需要維護其私有化變量。//通過 一個私有變量來控制是否 實例化對象, 初始化一個 init。 var Ext = {}; Ext.Base = (function () { //私有變量 控制返回的單體對象 var uniqInstance; //需要一個構(gòu)造器 init 初始化單體對象的方法 function Init () { //私有成員 var a1 = 10; var a2 = true; var fun1 = function () { console.log( a1 ); } return { attr1: a1, attr2: a2, fun1: fun1 } } return { getInstance: function () { if ( !uniqInstance ) { //不存在 ,創(chuàng)建單體實例 uniqInstance = new Init(); } return uniqInstance; } } })() var init = Ext.Base.getInstance(); init.fun1(); //10使用需求:創(chuàng)建一個對象并以某些數(shù)據(jù)對齊初始化,同時還要公開一些能夠訪問這些私有數(shù)據(jù)方法。
每個單例都是Object的實例,因為通過一個對象字面量表示單例通常都是 全局對象存在,不會將它傳遞給一個函數(shù)。增強的模塊模式
在返回對象之前假如對其增強的代碼。var application = function(){ //私有變量和函數(shù) var components = new Array(); //初始化 components.push(new BaseComponent()); //創(chuàng)建 application 的一個局部副本 var app = new BaseComponent(); //公共接口 app.getComponentCount = function(){ return components.length; } app.registerComponent = function(component){ if (typeof component == "object"){ components.push(component); } } //返回這個副本 return app; }();BOM window對象BOM的核心對象是window,表示瀏覽器的一個實例。
在瀏覽器中,window對象有雙重角色:
1:JavaScript訪問瀏覽器窗口的一個接口
2: ECMAScript規(guī)定的Global對象。
在網(wǎng)頁中定義的任何一個對象,變量,函數(shù),都已window作為其Global對象。因此有權(quán)訪問parseInt()等方法。窗口與框架
使用框架時,每個框架都有自己的window對象以及所有原生構(gòu)造函數(shù)及其它函數(shù)的副本。每個框架都保存在frames集合中,可以通過位置或通過名稱來訪問。
top對象始終指向最外圍的框架,也就是整個瀏覽器窗口。
parent 對象表示包含當(dāng)前框架的框架,而 self 對象則回指 window 。全局作用域
定義全局變量與在window對象上直接定義屬性差別:
全局變量不能通過delete操作刪除,直接定義window對象上的定義屬性可以刪除。var age = 22; window.color = "red"; //在 IE < 9 時拋出錯誤,在其他所有瀏覽器中都返回 false delete window.age; //在 IE < 9 時拋出錯誤,在其他所有瀏覽器中都返回 true delete window.color; // returns true console.log(window.age); //22 29 console.log(window.color); // undefinedvar 語句添加的window屬性[Configurable]的特性,這個特性的值被設(shè)置為false。所以定義的不可delete操作符刪除。
嘗試訪問未聲明的變量會拋出錯誤,但是通過window對象,可以知道某個可能未聲明的變量是否存在。
var newValue = oldValue; // 報錯,未定義 var newValue = window.oldValue; // 屬性查詢,不會拋出錯誤location對象提供了與當(dāng)前窗口中加載的文檔有關(guān)的信息,提供一些導(dǎo)航功能。
location對象很特別,即是window對象的屬性,也是document對象的屬性,window.location 和 document.location引用的是同一個對象。作用:保存著當(dāng)前文檔的信息,還表現(xiàn)將URL解析為獨立的片段。
屬性 例子 說明 hash "#contents" 返回URL中的hash(#號后跟零或多個字符),如果URL中不包含散列,則返回空字符串 host "www.aa.com:80" 返回服務(wù)器名稱和端口號(如果有) hostname "www.aa.com" 返回不帶端口號的服務(wù)器名稱 href "http:/www.aa.com" 返回當(dāng)前加載頁面的完整URL。而location對象的toString()方法也返回這個值 pathname "/WileyCDA/" 返回URL中的目錄和(或)文件名 port "8080" 返回URL中指定的端口號。如果URL中不包含端口號,則這個屬性返回空字符串 protocol "http:" 返回頁面使用的協(xié)議。通常是http:或https: search "?q=javascript" 返回URL的查詢字符串。這個字符串以問號開頭 位置操作
location對象改變?yōu)g覽器位置location.assign("http://segmentfault.com"); // 打開新的URL并在瀏覽器的歷史記錄中生成一條記錄。如果將location.href 或window.location設(shè)置為一個URL,會以該值調(diào)用assign()方法。
window.location = "http://www.segmentfault.com"; location.;最常用的方式:location.;
禁止用戶使用‘后退’按鈕 使用location.replace();
參數(shù):需要導(dǎo)航的URL
location.relaod(); 作用:重新加載當(dāng)前顯示的頁面location.reload(); //重新加載(有可能從瀏覽器緩存中加載) location.reload(true); //重新加載(從服務(wù)器重新加載)location.reload();調(diào)用之后的代碼可能會也可能不會執(zhí)行,取決于網(wǎng)絡(luò)延遲或系統(tǒng)資源等因素。如果使用location.relaod();最好放在代碼最后一行。
navigator對象作用:識別客戶端瀏覽器
navigator.userAgent // 瀏覽器的用戶代理字符串檢測插件
非IE下使用:navigator.plugins數(shù)組function hasPlugin(name){ name = name.toLowerCase(); for (var i=0; i < navigator.plugins.length; i++){ if (navigator. plugins [i].name.toLowerCase().indexOf(name) > -1){ return true; } } return false;注冊處理程序
registerContentHandler() 和 registerProtocolHandler()
作用:讓一個站點指明它可以處理特定類型的信息。(使用范圍:RSS 閱讀器和在線電子郵件程序)registerContentHandler();
參數(shù)1:要處理的MIME類型
參數(shù)2:可以處理該MIME類型的頁面URL
參數(shù)3:應(yīng)用程序的名稱// 站點注冊為處理 RSS 源的處理程序 navigator.registerContentHandler("application/rss+xml","http://www.somereader.com?feed=%s", "Some Reader"); // 參數(shù)1:RSS源 的MIME類型參數(shù) 2:應(yīng)該接收 RSS源 URL的 URL,其中的%s 表示RSS 源 URL,由瀏覽器自動插入。 // 作用:當(dāng)下一次請求 RSS 源時,瀏覽器就會打開指定的 URL,而相應(yīng)的Web 應(yīng)用程序?qū)⒁赃m當(dāng)方式來處理該請求。screen對象表明客戶端能力:瀏覽器窗口外部的顯示器信息,如:像素寬度和高度。
window.screen.height // 屏幕的像素高度
window.screen.windth // 屏幕的像素寬度window.resizeTo(); // 調(diào)整瀏覽器窗口大小
history對象history 對象:保存用戶上網(wǎng)的歷史記錄。充窗口被打開的那一刻算起。
history是window對象的屬性,因此每個瀏覽器串窗口,每個標(biāo)簽頁乃至每個框架,都有自己的history對象與特定的window對象關(guān)聯(lián)。history.go(); 在用戶的歷史記錄中任意跳轉(zhuǎn)。
//后退一頁 history.go(-1); //前進(jìn)一頁 history.go(1); //前進(jìn)兩頁 history.go(2); // go()參數(shù)是字符串 // 可能后退,也可能前進(jìn),具體要看哪個位置最近 //跳轉(zhuǎn)到最近的 wrox.com 頁面 history.go("segmentfault.com");history.length; 保存著歷史記錄的數(shù)量
高級技巧 高級函數(shù)
包括所有歷史記錄,即所有向后向前的記錄。對于加載到窗口,標(biāo)簽頁或框架中的第一個頁面而言。安全的類型檢測
JavaScript 中內(nèi)置的類型檢測機制并非完全可靠
typeof操作符,由于它有一些無法預(yù)知的行為,導(dǎo)致檢測數(shù)據(jù)類型時得到不靠譜的結(jié)果。(Safari直至第四版,對正則表達(dá)式 typeof 檢測 會返回 "function")
instanceof操作符,存在多個全局作用域(像一個頁面中包含多個frame)的情況下。
var isArray = valeu instaceof Array; // 返回true 條件:value 必須是數(shù)組, 必須與Array構(gòu)造函數(shù)在同個全局作用域中。(Array 是 window的屬性)。在任何值上調(diào)用Object原生的toString();方法。
返回 [object NativeConstructorName]格式的字符串。每個類在內(nèi)部都有一個[[Class]]屬性,這個屬性中指定了這個字符串中的構(gòu)造函數(shù)名.應(yīng)用:
檢測原生JSON對象。var isNativeJSON = window.JSON && Object.prototype.toString.call(JSON) === "[object JSON]";作用域安全的構(gòu)造函數(shù)
作用:自定義對象和構(gòu)造函數(shù)的定義和用法。
構(gòu)造函數(shù)就是一個使用new操作符調(diào)用的函數(shù)。當(dāng)使用new調(diào)用時,構(gòu)造函數(shù)內(nèi)用到的this對象會指向新創(chuàng)建的對象實例。問題:當(dāng)沒有使用new操作符來調(diào)用該構(gòu)造函數(shù)的情況下。this對象是在運行時綁定的,所以直接調(diào)用 類名 ,this會映射到全局對象window上,導(dǎo)致錯誤對象屬性的以外增加。
function Person ( name, age ) { this.name = name; this.age = age; } var p1 = Person("cyan", 22); console.log(window.name); console.log(window.age); // Person實例的屬性被加到window對象上,因為構(gòu)造函數(shù)時作為普通函數(shù)調(diào)用,忽略了new操作符。由this對象的晚綁定造成的,在這里this被解析成了window對象。// 在類中添加判斷: function Person ( name, age ) { if ( this instanceof Person ) { this.name = name; this.age = age; } else { return new Person(name, age); } } var p1 = Person("cyan", 22); console.log(window.name); console.log(window.age); // 添加一個檢查,并確保this對象是Person實例。 // 要么使用new操作符,要么使用現(xiàn)有的Person實例環(huán)境中調(diào)用構(gòu)造函數(shù)。函數(shù)綁定
函數(shù)綁定要創(chuàng)建一個函數(shù),可以在待定的this環(huán)境中指定參數(shù)調(diào)用另一個函數(shù)。
使用范圍:回調(diào)函數(shù)與事件處理程序一起使用,(在將函數(shù)作為變量傳遞的同時保留代碼執(zhí)行環(huán)境)// bind(); 函數(shù) function bind ( fn, context ) { return function () { return fn.apply(context, arguments); } }將某個函數(shù)以值的形式進(jìn)行傳遞,同時該函數(shù)必須在特定環(huán)境中執(zhí)行,被綁定的函數(shù)的效果。
主要用于事件處理程序以及setTimeout() 和 setInterval();
被綁定函數(shù)與普通函數(shù)相比有更多開銷,需要更多內(nèi)存支持,同時也因為多重函數(shù)調(diào)用調(diào)用比較慢。最好只在必要時使用。函數(shù)柯里化
函數(shù)柯里化( function currying ): 把接受多個參數(shù)的函數(shù)變換成接受一個單一參數(shù)(最初函數(shù)的第一個參數(shù))的函數(shù),并返回接受余下的參數(shù)且返回結(jié)果的新函數(shù)
作用:創(chuàng)建已經(jīng)設(shè)置好的一個或多個參數(shù)的函數(shù)。
函數(shù)柯里化的基本方法:使用一個閉包返回一個函數(shù)。
函數(shù)柯里化和函數(shù)綁定區(qū)別:當(dāng)函數(shù)被調(diào)用時,返回的函數(shù)還需要設(shè)置一些傳入的參數(shù)。
函數(shù)柯里化的動態(tài)創(chuàng)建:調(diào)用另一個函數(shù)并為它傳入要柯里化的函數(shù)和必要的參數(shù)。
用處:
作為函數(shù)綁定的一部分包含在其中,構(gòu)造出更為復(fù)雜函數(shù)function bind ( fn, context ) { let args = Array.prototype.slice.call(arguments, 2); return { let innerArgs = Array.prototype.slice.call(arguments); let fianlArgs = args.concat(innerArgs); return fn.apply(context, fianlArgs); } }缺點:
每個函數(shù)都會帶來額外的開銷.不能夠濫用。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/86424.html
showImg(http://img3.douban.com/lpic/s8958650.jpg); 0x00 javascript組成 ECMAScript(-265)核心語言部分 DOM文檔對象模型(DOM1、2、3) BOM瀏覽器對象模型(提供與瀏覽器交互的接口和方法) 0x01 async 異步加載 執(zhí)行順序不定 charset defer 延遲加載,立即下載腳本但不執(zhí)行 src ...
摘要:客戶端檢測方式能力檢測怪癖檢測用戶代理檢測能力檢測最常用也是最為人們廣泛接受的客戶端檢測形式是能力檢測又稱特性檢測。在可能的情況下,盡量使用進(jìn)行能力檢測。 客戶端檢測方式 能力檢測 怪癖檢測 用戶代理檢測 能力檢測 最常用也是最為人們廣泛接受的客戶端檢測形式是能力檢測(又稱特性檢測)。能力檢測的目標(biāo)不是識別特定的瀏覽器,而是識別瀏覽器的能力。采用這種方式不必顧及特定的瀏覽器如何...
摘要:由構(gòu)造函數(shù)返回的對象就是表達(dá)式的結(jié)果。如果構(gòu)造函數(shù)沒有顯式返回一個對象,則使用步驟創(chuàng)建的對象。運算符返回一個布爾值,表示對象是否為某個構(gòu)造函數(shù)的實例。 面向?qū)ο?本人能力有限,有誤請斧正 本文旨在復(fù)習(xí)面向?qū)ο?不包含es6) 本文學(xué)習(xí)思維 創(chuàng)建對象的方式,獲取對象屬性 構(gòu)造函數(shù),構(gòu)造函數(shù)的new 做了什么 原型與原型對象 原型鏈 繼承(借用構(gòu)造繼承、原型繼承、組合繼承、寄生組合繼承)...
摘要:事情是如何發(fā)生的最近干了件事情,發(fā)現(xiàn)了源碼的一個。樓主找到的關(guān)于和區(qū)別的資料如下關(guān)于拿來主義為什么這么多文章里會出現(xiàn)澤卡斯的錯誤代碼樓主想到了一個詞,叫做拿來主義。的文章,就深刻抨擊了拿來主義這一現(xiàn)象。 事情是如何發(fā)生的 最近干了件事情,發(fā)現(xiàn)了 underscore 源碼的一個 bug。這件事本身并沒有什么可說的,但是過程值得我們深思,記錄如下,各位看官仁者見仁智者見智。 平時有瀏覽別...
閱讀 3030·2021-11-24 10:32
閱讀 688·2021-11-24 10:19
閱讀 5134·2021-08-11 11:17
閱讀 1467·2019-08-26 13:31
閱讀 1268·2019-08-23 15:15
閱讀 2293·2019-08-23 14:46
閱讀 2277·2019-08-23 14:07
閱讀 1095·2019-08-23 14:03