JavaScript高階程式設計(讀後感-持續更新)

慕斯不想說話發表於2019-01-03

1、判斷傳過來的變數是否是一個陣列
很多時候我們都會用

var isArray = value instanceof Array;

這個方法在一般情況下(value不是從其他frame傳過來的)是成立的。 但是為了防止一般情況的發生,我們可以用該方法來驗證。
function isArray(value) {
	return Object.prototype.toString.call(value) == "[object Array]";
}
複製程式碼

4、typeof和instanceof的區別

typeof

是一種操作符而不是一個函式,用來檢測給定變數的資料型別。一般是用來判斷"number"、"string"、"boolean"、 和 "undefined"這4種基本資料型別的。object型別用typeof來判斷結果存在爭議,所以一般是使用instanceof來進行判斷。引用型別用typeof來進行判斷時返回的都是object,以至於我們還是不知道用於判斷的變數到到底是什麼資料型別。

如typeof(null)和typeof([1,2])返回的都是object,但實際上null是Null型別,而[1,2]是Array型別.

typeof 最常見的用法應該是用來判斷某個變數是否未定義和變數是否初始化。

    if(typeof(b)=="undefined"){}
    不能直接用if(b)去判斷,這樣程式碼會直接報錯,後面的程式碼也會執行不了。
複製程式碼

參考文章:
www.cnblogs.com/Trr-9846881…

因為typeof判斷資料型別的不足(只能用於判斷基本資料型別),引用資料型別需要使用instanceof的方式來進行判斷。

instanceof

此運算子可以判斷一個變數是否是某個物件(類)的例項,換句話說用來檢測 constructor.prototype 是否存在於引數 object 的原型鏈上。。 下面通過程式碼闡述instanceof的內部機制,假設現在有 x instanceof y 一條語句,則其內部實際做了如下判斷:

while(x.__proto__!==null) {
    if(x.__proto__===y.prototype) {
        return true;
    }
    x.__proto__ = x.__proto__.proto__;
}
if(x.__proto__==null) {return false;}
複製程式碼

參考連結:juejin.im/post/5c19c1…

x會一直沿著隱式原型鏈__proto__向上查詢直到x.__proto__.__proto__......===y.prototype為止,如果找到則返回true,也就是x為y的一個例項。否則返回false,x不是y的例項。

5.原型和原型連

prototype(原型),是一個指標,指向一個物件, 而這個物件的用途是包含可以由特定型別的所有例項共享的屬性和方法。當呼叫建構函式建立一個新例項後,該例項的內部將包含一個指標(內部 屬性),指向建構函式的原型物件。

function Person(name, age, job){    
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function("alert(this.name)"); // 與宣告函式在邏輯上是等價的
}
複製程式碼

當呼叫建構函式建立一個新例項後,該例項的內部將包含一個指標(內部屬性),指向建構函式的原型物件,而不是存在於例項與建構函式之間。

var p=new Person("李四",24,"程式設計師");
p.__proto__=Person.prototype;
複製程式碼

JavaScript高階程式設計(讀後感-持續更新)

上圖展示了 Person 建構函式、 Person 的原型屬性以及 Person 現有的兩個例項之間的關係。雖然在所有實現中都無法訪問到Prototype,但可以通過 isPrototypeOf()方法來確定物件之間是否存在這種關係。

alert(Person.prototype.isPrototypeOf(person1)); //true
alert(Person.prototype.isPrototypeOf(person2)); //true
複製程式碼

ECMAScript 5 增加了一個新方法,叫 Object.getPrototypeOf(),在所有支援的實現中,這個方法返回Prototype的值。例如:

alert(Object.getPrototypeOf(person1) == Person.prototype); //true
alert(Object.getPrototypeOf(person1).name); //"Nicholas"
複製程式碼

每當程式碼讀取某個物件的某個屬性時,首先會在該物件上搜尋有沒有改屬性,沒有的話才會去原型上搜尋。

注意:雖然可以通過物件例項訪問儲存在原型中的值,但卻不能通過物件例項重寫原型中的值。如果我們在例項中新增了一個屬性,而該屬性與例項原型中的一個屬性同名,那我們就在例項中建立該屬性,該屬性將會遮蔽原型中的那個屬性。請看下面的例子:

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"—— 來自例項
alert(person2.name); //"Nicholas"—— 來自原型
複製程式碼

使用 hasOwnProperty()方法可以檢測一個屬性是存在於例項中,還是存在於原型中。

in 操作符會在通 過物件能夠訪問給定屬性時返回true,無論該屬性存在於例項中還是原型中。但是可以同時使用 hasOwnProperty()方法和 in 操作符,就可以確定該屬性到底是存在於物件中,還是存在於 原型中。

var person1 = new Person();
var person2 = new Person();
alert(person1.hasOwnProperty("name")); //false
alert("name" in person1); //true
person1.name = "Greg";
alert(person1.name); //"Greg" —— 來自例項
alert(person1.hasOwnProperty("name")); //true
alert("name" in person1); //true
alert(person2.name); //"Nicholas" —— 來自原型
alert(person2.hasOwnProperty("name")); //false
alert("name" in person2); //true
delete person1.name;
alert(person1.name); //"Nicholas" —— 來自原型
alert(person1.hasOwnProperty("name")); //false
alert("name" in person1); //true
複製程式碼

要取得物件上所有可列舉的例項屬性,可以使用 ECMAScript 5 的 Object.keys()方法。如果你想要得到所有例項屬性,無論它是否可列舉,都可以使用 Object.getOwnPropertyNames() 方法。

var keys = Object.keys(Person.prototype);
alert(keys); //"name,age,job,sayName"
var p1 = new Person();
p1.name = "Rob";
p1.age = 31;
var p1keys = Object.keys(p1);
alert(p1keys); //"name,age"
var keys = Object.getOwnPropertyNames(Person.prototype
alert(keys); //"constructor,name,age,job,sayName"
複製程式碼

使用物件字面量來重寫整個原型物件時,本質上完全重寫了預設的 prototype 物件,因此 constructor 屬性也就變成了新物件的 constructor 屬性(指向 Object 建構函式),不再指向 Person 函式。此時,儘管 instanceof 操作符還能返回正確的結果,但通過 constructor 已經無法確定物件的型別了,

function Person() {}
var friend2 = new Person();
Person.prototype = {
	//constructor : Person,
	name: "Nicholas",
	age: 29,
	job: "Software Engineer",
	sayName: function() {
		alert(this.name);
	}
};
var friend = new Person();
console.log(friend2 instanceof Object); //true
console.log(friend2 instanceof Person); //false
console.log(friend2.constructor == Person); //true
console.log(friend2.constructor == Object); //false

console.log(friend instanceof Object); //true
console.log(friend instanceof Person); //true
console.log(friend.constructor == Person); //false
console.log(friend.constructor == Object); //true
複製程式碼

由於原型的動態性,呼叫建構函式時會為例項新增一個指向最初原型的Prototype指標,而把原型修改為另外一個物件就等於切斷了建構函式與最初原型之間的聯絡。看下面的例子

function Person(){
}
var friend = new Person();
Person.prototype = {
    constructor: Person,
    name : "Nicholas",
    age : 29,
    job : "Software Engineer",
    sayName : function () {
        alert(this.name);
    }
};
var friend2=new Person();
friend.sayName(); //Uncaught TypeError: friend.sayName is not a function 
friend2.sayName();//Nicholas
console.log(friend instanceof Person);//false
console.log(friend instanceof Object);//true
console.log(friend2 instanceof Person);//true
複製程式碼

這是因為friend的prototype指向的是沒重寫Person.prototype之前的Person.prototype,也就是建構函式最初的原型物件。而friend的prototype指向的是重寫Person.prototype後的Person.prototype。如下圖所示

JavaScript高階程式設計(讀後感-持續更新)

原型鏈:基本思想是利用原 型讓一個引用型別繼承另一個引用型別的屬性和方法。最直觀的表現就是讓原型物件等於另一個型別的例項。

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();
alert(instance.getSuperValue()); //true
複製程式碼

使得原來存在於 SuperType 的例項中的所有屬性和方法,現在也存在於 SubType.prototype 中。

SubType.prototype = new SuperType();
複製程式碼

使得instance的constructor指向了SuperType。

console.log(instance.constructor===SuperType);//true
複製程式碼

總結:

訪問一個例項屬性時,首先會在例項中搜尋該屬性。如果沒有找到該屬性,則會繼續搜尋例項的原型。在通過原型鏈實現繼承的情況下,搜尋過程就得以沿著原型鏈繼續向上。就拿上面的例子來說,呼叫 instance.getSuperValue()會經歷三個搜尋步驟:

  1. 搜尋例項;
  2. 搜尋 SubType.prototype;
  3. 搜尋 SuperType.prototype,最後一步才會找到該方法。

在找不到屬性或方法的情況下,搜尋過程總是要一環一環地前行到原型鏈末端才會停下來。

相關文章