日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

一切皆對象

咱們經常聽到JS中“一切皆對象”?有沒有問想過這是什么意思?其它語言也有“一切皆對象”之說,如Python。但是Python中的對象不僅僅是像JS對象這樣的存放值和值的容器。Python中的對象是一個。JS中有類似的東西,但JS中的“對象”只是鍵和值的容器:

var obj = { name: "Tom", age: 34 }

 

實際上,JS中的對象是一種“啞”類型,但很多其他實體似乎都是從對象派生出來的。甚至是數組,在JS中創建一個數組,如下所示:

var arr = [1,2,3,4,5]

 

然后用typeof運算符檢查類型,會看到一個令人驚訝的結果:

typeof arr
"object"

看來數組是一種特殊的對象!即使JS中的函數也是對象。如果你深入挖掘,還有更多,創建一個函數,該函數就會附加一些方法:

var a = function(){ return false; }
a.toString()

輸出:

"function(){ return false; }"

 

咱們并沒有在函數聲明toString方法,所以在底層一定還有東西。它從何而來?Object有一個名為.toString的方法。似乎咱們的函數具有相同的Object方法。

Object.toString()

 

這時咱們使用瀏覽器控制臺來查看默認被附加的函數和屬性,這個謎團就會變得更加復雜:

JS 對象生命周期的秘密

 

誰把這些方法放在函數呢。 JS中的函數是一種特殊的對象,這會不會是個暗示? 再看看上面的圖片:我們的函數中有一個名為prototype的奇怪命名屬性,這又是什么鬼?

JS中的prototype是一個對象。它就像一個背包,附著在大多數JS內置對象上。例如 Object, Function, Array, Date, Error,都有一個“prototype”:

typeof Object.prototype // 'object'
typeof Date.prototype // 'object'
typeof String.prototype // 'object'
typeof Number.prototype // 'object'
typeof Array.prototype // 'object'
typeof Error.prototype // 'object'

注意內置對象有大寫字母:

  • String
  • Number
  • Boolean
  • Object
  • Symbol
  • Null
  • Undefined

以下除了Object是類型之外,其它是JS的基本類型。另一方面,內置對象就像JS類型的鏡像,也用作函數。例如,可以使用String作為函數將數字轉換為字符串:

String(34)

 

現在回到“prototype”。prototype是所有公共方法和屬性的宿主,從祖先派生的“子”對象可以從使用祖先的方法和屬性。也就是說,給定一個原始 prototype,咱們可以創建新的對象,這些對象將使用一個原型作為公共函數的真實源,不 Look see see。

假設有個要求創建一個聊天應用程序,有個人物對象。這個人物可以發送消息,登錄時,會收到一個問候。

根據需求咱們很容易定義這個么一 Person 對象:

var Person = {
 name: "noname",
 age: 0,
 greet: function() {
 console.log(`Hello ${this.name}`);
 }
};

你可能會想知道,為什么這里要使用字面量的方式來聲明 Person 對象。稍后會詳細說明,現在該 Person 為“模型”。通過這個模型,咱們使用 Object.create() 來創建以為這個模型為基礎的對象。

創建和鏈接對象

JS中對象似乎以某種方式鏈接在一起,Object.create()說明了這一點,此方法從原始對象開始創建新對象,再來創建一個新Person 對象:

var Person = {
 name: "noname",
 age: 0,
 greet: function() {
 console.log(`Hello ${this.name}`);
 }
};
var Tom = Object.create(Person);

現在,Tom 是一個新的對象,但是咱們沒有指定任何新的方法或屬性,但它仍然可以訪問Person中的name和age 屬性。

var Person = {
 name: "noname",
 age: 0,
 greet: function() {
 console.log(`Hello ${this.name}`);
 }
};
var Tom = Object.create(Person);
var tomAge = Tom.age;
var tomName = Tom.name;
console.log(`${tomAge} ${tomName}`);
// Output: 0 noname

現在,可以從一個共同的祖先開始創建新的person。但奇怪的是,新對象仍然與原始對象保持連接,這不是一個大問題,因為“子”對象可以自定義屬性和方法

var Person = {
 name: "noname",
 age: 0,
 greet: function() {
 console.log(`Hello ${this.name}`);
 }
};
var Tom = Object.create(Person);
Tom.age = 34;
Tom.name = "Tom";
var tomAge = Tom.age;
var tomName = Tom.name;
console.log(`${tomAge} ${tomName}`);
// Output: 34 Tom

這種方式被稱為“屏蔽”原始屬性。還有另一種將屬性傳遞給新對象的方法。Object.create將另一個對象作為第二個參數,可以在其中為新對象指定鍵和值:

var Tom = Object.create(Person, {
 age: {
 value: 34
 },
 name: {
 value: "Tom"
 }
});

以這種方式配置的屬性默認情況下不可寫,不可枚舉,不可配置。不可寫意味著之后無法更改該屬性,更改會被忽略:

var Tom = Object.create(Person, {
 age: {
 value: 34
 },
 name: {
 value: "Tom"
 }
});
Tom.age = 80;
Tom.name = "evilchange";
var tomAge = Tom.age;
var tomName = Tom.name;
Tom.greet();
console.log(`${tomAge} ${tomName}`);
// Hello Tom
// 34 Tom

不可枚舉意味著屬性不會在 for...in 循環中顯示,例如:

for (const key in Tom) {
 console.log(key);
}
// Output: greet

但是正如咱們所看到的,由于JS引擎沿著原型鏈向上查找,在“父”對象上找到greet屬性。最后,不可配置意味著屬性既不能修改也不能刪除。

Tom.age = 80;
Tom.name = "evilchange";
delete Tom.name;
var tomAge = Tom.age;
var tomName = Tom.name;
console.log(`${tomAge} ${tomName}`);
// 34 Tom

如果要更改屬性的行為,只需配writable(可寫性),configurable(可配置),enumerable(可枚舉)屬性即可。

var Tom = Object.create(Person, {
 age: {
 value: 34,
 enumerable: true,
 writable: true,
 configurable: true
 },
 name: {
 value: "Tom",
 enumerable: true,
 writable: true,
 configurable: true
 }
});

現在,Tom也可以通過以下方式訪問greet():

var Person = {
 name: "noname",
 age: 0,
 greet: function() {
 console.log(`Hello ${this.name}`);
 }
};
var Tom = Object.create(Person);
Tom.age = 34;
Tom.name = "Tom";
var tomAge = Tom.age;
var tomName = Tom.name;
Tom.greet();
console.log(`${tomAge} ${tomName}`);
// Hello Tom
// 34 Tom

暫時不要過于擔心“this”。拉下來會詳細介紹。暫且先記住,“this”是對函數執行的某個對象的引用。在咱們的例子中,greet() 在Tom的上下文中運行,因此可以訪問“this.name”。

構建JAVAScript對象

目前為止,只介紹了關于“prototype”的一點知識 ,還有玩了一會 Object.create()之外但咱們沒有直接使用它。隨著時間的推移出現了一個新的模式:構造函數。使用函數創建新對象聽起來很合理, 假設你想將Person對象轉換為函數,你可以用以下方式:

function Person(name, age) {
 var newPerson = {};
 newPerson.age = age;
 newPerson.name = name;
 newPerson.greet = function() {
 console.log("Hello " + newPerson.name);
 };
 return newPerson;
}

因此,不需要到處調用object.create(),只需將Person作為函數調用:

var me = Person("Valentino");

 

構造函數模式有助于封裝一系列JS對象的創建和配置。在這里, 咱們使用字面量的方式創建對象。這是一種從面向對象語言借用的約定,其中類名開頭要大寫。

上面的例子有一個嚴重的問題:每次咱們創建一個新對象時,一遍又一遍地重復創建greet()函數。可以使用Object.create(),它會在對象之間創建鏈接,創建次數只有一次。首先,咱們將greet()方法移到外面的一個對象上。然后,可以使用Object.create()將新對象鏈接到該公共對象:

var personMethods = {
 greet: function() {
 console.log("Hello " + this.name);
 }
};
function Person(name, age) {
 // greet lives outside now
 var newPerson = Object.create(personMethods);
 newPerson.age = age;
 newPerson.name = name;
 return newPerson;
}
var me = Person("Valentino");
me.greet();
// Output: "Hello Valentino"

這種方式比剛開始會點,還可以進一步優化就是使用prototype,prototype是一個對象,可以在上面擴展屬性,方法等等。

Person.prototype.greet = function() {
 console.log("Hello " + this.name);
};

移除了personMethods。調整Object.create的參數,否則新對象不會自動鏈接到共同的祖先:

function Person(name, age) {
 // greet lives outside now
 var newPerson = Object.create(Person.prototype);
 newPerson.age = age;
 newPerson.name = name;
 return newPerson;
}
Person.prototype.greet = function() {
 console.log("Hello " + this.name);
};
var me = Person("Valentino");
me.greet();
// Output: "Hello Valentino"

現在公共方法的來源是Person.prototype。使用JS中的new運算符,可以消除Person中的所有噪聲,并且只需要為this分配參數。

下面代碼:

function Person(name, age) {
 // greet lives outside now
 var newPerson = Object.create(Person.prototype);
 newPerson.age = age;
 newPerson.name = name;
 return newPerson;
}

改成:

function Person(name, age) {
 this.name = name;
 this.age = age;
}

完整代碼:

function Person(name, age) {
 this.name = name;
 this.age = age;
}
Person.prototype.greet = function() {
 console.log("Hello " + this.name);
};
var me = new Person("Valentino");
me.greet();
// Output: "Hello Valentino"

注意,使用new關鍵字,被稱為“構造函數調用”,new 干了三件事情

  • 創建一個空對象
  • 將空對象的proto指向構造函數的prototype
  • 使用空對象作為上下文的調用構造函數
  • function Person(name, age) {
  • this.name = name;
  • this.age = age;
  • }

根據上面描述的,new Person("Valentino") 做了:

  • 創建一個空對象:var obj = {}
  • 將空對象的proto__`指向構造函數的 prototype:`obj.__proto = Person().prototype
  • 使用空對象作為上下文調用構造函數:Person.call(obj)

檢查原型鏈

檢查JS對象之間的原型鏈接有很多種方法。例如,Object.getPrototypeOf是一個返回任何給定對象原型的方法。考慮以下代碼:

var Person = {
 name: "noname",
 age: 0,
 greet: function() {
 console.log(`Hello ${this.name}`);
 }
};
var Tom = Object.create(Person);

檢查Person是否是Tom的原型:

var tomPrototype = Object.getPrototypeOf(Tom);
console.log(tomPrototype === Person);
// Output: true

當然,如果使用構造函數調用構造對象,Object.getPrototypeOf也可以工作。但是應該檢查原型對象,而不是構造函數本身:

function Person(name, age) {
 this.name = name;
 this.age = age;
}
Person.prototype.greet = function() {
 console.log("Hello " + this.name);
};
var me = new Person("Valentino");
var mePrototype = Object.getPrototypeOf(me);
console.log(mePrototype === Person.prototype);
// Output: true

除了Object.getPrototypeOf之外,還有另一個方法isPrototypeOf。該方法用于測試一個對象是否存在于另一個對象的原型鏈上,如下所示,檢查 me 是否在 Person.prototype 上:

Person.prototype.isPrototypeOf(me) && console.log('Yes I am!')

 

instanceof運算符也可以用于測試構造函數的prototype屬性是否出現在對象的原型鏈中的任何位置。老實說,這個名字有點誤導,因為JS中沒有“實例”。在真正的面向對象語言中,實例是從類創建的新對象。請考慮Python中的示例。咱們有一個名為Person的類,咱們從該類創建一個名為“tom”的新實例:

class Person():
 def __init__(self, age, name):
 self.age = age;
 self.name = name;
 def __str__(self):
 return f'{self.name}'
tom = Person(34, 'Tom')

注意,在Python中沒有new關鍵字。現在,咱們可以使用isinstance方法檢查tom是否是Person的實例

isinstance(tom, Person)
// Output: True

Tom也是Python中“object”的一個實例,下面的代碼也返回true:

isinstance(tom, object)
// Output: True

根據isinstance文檔,“如果對象參數是類參數的實例,或者是它的(直接、間接或虛擬)子類的實例,則返回true”。咱們在這里討論的是類。現在讓咱們看看instanceof做了什么。咱們將從JS中的Person函數開始創建tom(因為沒有真正的類)

function Person(name, age) {
 this.name = name;
 this.age = age;
}
Person.prototype.greet = function() {
 console.log(`Hello ${this.name}`);
};
var tom = new Person(34, "Tom");

使用isinstance方法檢查tom是否是Person和 Object 的實例

if (tom instanceof Object) {
 console.log("Yes I am!");
}
if (tom instanceof Person) {
 console.log("Yes I am!");
}

因此,可以得出結論:JS對象的原型總是連接到直接的“父對象”和Object.prototype。沒有像Python或Java這樣的類。JS是由對象組成,那么什么是原型鏈呢?如果你注意的話,咱們提到過幾次“原型鏈”。JS對象可以訪問代碼中其他地方定義的方法,這看起來很神奇。再次考慮下面的例子:

var Person = {
 name: "noname",
 age: 0,
 greet: function() {
 console.log(`Hello ${this.name}`);
 }
};
var Tom = Object.create(Person);
Tom.greet();

即使該方法不直接存在于“Tom”對象上,Tom也可以訪問greet()。

這是JS的一個內在特征,它從另一種稱為Self的語言中借用了原型系統。當訪問greet()時,JS引擎會檢查該方法是否可直接在Tom上使用。如果不是,搜索將繼續向上鏈接,直到找到該方法。

“鏈”是Tom連接的原型對象的層次結構。在我們的例子中,Tom是Person類型的對象,因此Tom的原型連接到Person.prototype。而Person.prototype是Object類型的對象,因此共享相同的Object.prototype原型。如果在Person.prototype上沒有greet(),則搜索將繼續向上鏈接,直到到達Object.prototype。這就是咱們所說的“原型鏈”

保護對象不受操縱

大多數情況下,JS 對象“可擴展”是必要的,這樣咱們可以向對象添加新屬性。但有些情況下,我們希望對象不受進一步操縱。考慮一個簡單的對象:

var superImportantObject = {
 property1: "some string",
 property2: "some other string"
};

默認情況下,每個人都可以向該對象添加新屬性

var superImportantObject = {
 property1: "some string",
 property2: "some other string"
};
superImportantObject.anotherProperty = "Hei!";
console.log(superImportantObject.anotherProperty); // Hei!

Object.preventExtensions()方法讓一個對象變的不可擴展,也就是永遠不能再添加新的屬性。

var superImportantObject = {
 property1: "some string",
 property2: "some other string"
};
Object.preventExtensions(superImportantObject);
superImportantObject.anotherProperty = "Hei!";
console.log(superImportantObject.anotherProperty); // undefined

這種技術對于“保護”代碼中的關鍵對象非常方便。JS 中還有許多預先創建的對象,它們都是為擴展而關閉的,從而阻止開發人員在這些對象上添加新屬性。這就是“重要”對象的情況,比如XMLHttpRequest的響應。瀏覽器供應商禁止在響應對象上添加新屬性

var request = new XMLHttpRequest();
request.open("GET", "https://jsonplaceholder.typicode.com/posts");
request.send();
request.onload = function() {
 this.response.arbitraryProp = "我是新添加的屬性";
 console.log(this.response.arbitraryProp); // undefined
};

這是通過在“response”對象上內部調用Object.preventExtensions來完成的。您還可以使用Object.isExtensible方法檢查對象是否受到保護。如果對象是可擴展的,它將返回true:

var superImportantObject = {
 property1: "some string",
 property2: "some other string"
};
Object.isExtensible(superImportantObject) && console.log("我是可擴展的");

如果對象不可擴展的,它將返回false:

var superImportantObject = {
 property1: "some string",
 property2: "some other string"
};
Object.preventExtensions(superImportantObject);
Object.isExtensible(superImportantObject) ||
 console.log("我是不可擴展的!");

當然,對象的現有屬性可以更改甚至刪除

var superImportantObject = {
 property1: "some string",
 property2: "some other string"
};
Object.preventExtensions(superImportantObject);
delete superImportantObject.property1;
superImportantObject.property2 = "yeees";
console.log(superImportantObject); // { property2: 'yeees' }

現在,為了防止這種操作,可以將每個屬性定義為不可寫和不可配置。為此,有一個方法叫Object.defineProperties。

var superImportantObject = {};
Object.defineProperties(superImportantObject, {
 property1: {
 configurable: false,
 writable: false,
 enumerable: true,
 value: "some string"
 },
 property2: {
 configurable: false,
 writable: false,
 enumerable: true,
 value: "some other string"
 }
});

或者,更方便的是,可以在原始對象上使用Object.freeze:

var superImportantObject = {
 property1: "some string",
 property2: "some other string"
};
Object.freeze(superImportantObject);

Object.freeze工作方式與Object.preventExtensions相同,并且它使所有對象的屬性不可寫且不可配置。唯一的缺點是“Object.freeze”僅適用于對象的第一級:嵌套對象不受操作的影響。

class

有大量關于ES6 類的文章,所以在這里只討論幾點。JS是一種真正的面向對象語言嗎?看起來是這樣的,如果咱們看看這段代碼

class Person {
 constructor(name) {
 this.name = name;
 }
 greet() {
 console.log(`Hello ${this.name}`);
 }
}

語法與Python等其他編程語言中的類非常相似:

class Person:
 def __init__(self, name):
 self.name = name
 def greet(self):
 return 'Hello' + self.name

或 php

class Person {
 public $name; 
 public function __construct($name){
 $this->name = $name;
 }
 public function greet(){
 echo 'Hello ' . $this->name;
 }
}

ES6中引入了類。但是在這一點上,咱們應該清楚JS中沒有“真正的”類。一切都只是一個對象,盡管有關鍵字class,“原型系統”仍然存在。新的JS版本是向后兼容的,這意味著在現有功能的基礎上添加了新功能,這些新功能中的大多數都是遺留代碼的語法糖。

總結

JS中的幾乎所有東西都是一個對象。從字面上看。JS對象是鍵和值的容器,也可能包含函數。Object是JS中的基本構建塊:因此可以從共同的祖先開始創建其他自定義對象。然后咱們可以通過語言的內在特征將對象鏈接在一起:原型系統。

從公共對象開始,可以創建共享原始“父”的相同屬性和方法的其他對象。但是它的工作方式不是通過將方法和屬性復制到每個孩子,就像OOP語言那樣。在JS中,每個派生對象都保持與父對象的連接。使用Object.create或使用所謂的構造函數創建新的自定義對象。與new關鍵字配對,構造函數類似于模仿傳統的OOP類。

思考

  • 如何創建不可變的 JS 對象?
  • 什么是構造函數調用?
  • 什么是構造函數?
  • “prototype” 是什么?
  • 可以描述一下 new 在底層下做了哪些事嗎?

作者:valentinogagliardi

譯者:前端小智

來源:github

分享到:
標簽:對象 JS
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定