国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

new 操作符到底做了什么?

_ivan / 1280人閱讀

摘要:原文相信很多才接觸前端的小伙伴甚至工作幾年的前端小伙伴對(duì)這個(gè)操作符的了解還停留在一知半解的地步,比較模糊。

原文:http://blog.xieyangogo.cn/201...

相信很多才接觸前端的小伙伴甚至工作幾年的前端小伙伴對(duì)new這個(gè)操作符的了解還停留在一知半解的地步,比較模糊。

就比如前不久接觸到一個(gè)入職兩年的前端小伙伴,他告訴我new是用來創(chuàng)建對(duì)象的,無可厚非,可能很多人都會(huì)這么答!

那這么答到底是錯(cuò)很是對(duì)呢?

下面我們?nèi)鎭碛懻撘幌逻@個(gè)問題:

我們要拿到一個(gè)對(duì)象,有很多方式,其中最常見的一種便是對(duì)象字面量:

var obj = {}
但是從語法上來看,這就是一個(gè)賦值語句,是把對(duì)面字面量賦值給了obj這個(gè)變量(這樣說或許不是很準(zhǔn)確,其實(shí)這里是得到了一個(gè)對(duì)象的實(shí)例?。。?p>很多時(shí)候,我們說要?jiǎng)?chuàng)建一個(gè)對(duì)象,很多小伙伴雙手一摸鍵盤,啪啪幾下就敲出了這句代碼。

上面說了,這句話其實(shí)只是得到了一個(gè)對(duì)象的實(shí)例,那這句代碼到底還能不能和創(chuàng)建對(duì)象畫上等號(hào)呢?

我們繼續(xù)往下看。

要拿到一個(gè)對(duì)象的實(shí)例,還有一種和對(duì)象字面量等價(jià)的做法就是構(gòu)造函數(shù):

var obj = new Object()
這句代碼一敲出來,相信小伙伴們對(duì)剛才我說的obj只是一個(gè)實(shí)例對(duì)象沒有異議了吧!

那很多小伙伴又會(huì)問了:這不就是new了一個(gè)新對(duì)象出來嘛!

沒錯(cuò),這確實(shí)是new了一個(gè)新對(duì)象出來,因?yàn)閖avascript之中,萬物解釋對(duì)象。

obj是一個(gè)對(duì)象,而且是通過new運(yùn)算符得到的,所以說很多小伙伴就肯定的說:new就是用來創(chuàng)建對(duì)象的!

這就不難解釋很多人把創(chuàng)建對(duì)象和實(shí)例化對(duì)象混為一談!!

我們?cè)趽Q個(gè)思路看看:既然js一切皆為對(duì)象,那為什么還需要?jiǎng)?chuàng)建對(duì)象呢?本身就是對(duì)象,我們何來創(chuàng)建一說?那我們可不可以把這是一種繼承呢?

說了這么多,相信不少伙伴已經(jīng)看暈了,但是我們的目的就是一個(gè):理清new是來做繼承的而不是所謂的創(chuàng)建對(duì)象?。?/p>

那繼承得到的實(shí)例對(duì)象有什么特點(diǎn)呢?

訪問構(gòu)造函數(shù)里面的屬性

訪問原型鏈上的屬性

下面是一段經(jīng)典的繼承,通過這段代碼來熱熱身,好戲馬上開始:

function Person(name, age) {
  this.name = name
  this.age = age
  this.gender = "男"
}

Person.prototype.nation = "漢"

Person.prototype.say = function() {
  console.log(`My name is ${this.age}`)
}

var person = new Person("小明", 25)

console.log(person.name)
console.log(person.age)
console.log(person.gender)
console.log(person.nation)

person.say()

現(xiàn)在我們來解決第一個(gè)問題:我們可以通過什么方式實(shí)現(xiàn)訪問到構(gòu)造函數(shù)里面的屬性呢?答案是callapply

function Parent() {
  this.name = ["A", "B"]
}

function Child() {
  Parent.call(this)
}

var child = new Child()
console.log(child.name) // ["A", "B"]

child.name.push("C")
console.log(child.name) // ["A", "B", "C"]

第一個(gè)問題解決了,那我們又來解決第二個(gè):那又怎么訪問原型鏈上的屬性呢?答案是__proto__

現(xiàn)在我們把上面那段熱身代碼稍加改造,不使用new來創(chuàng)建實(shí)例:

function Person(name, age) {
  this.name = name
  this.age = age
  this.gender = "男"
}

Person.prototype.nation = "漢"

Person.prototype.say = function() {
  console.log(`My name is ${this.age}`)
}

// var person = new Person("小明", 25)
var person = New(Person, "小明", 25)

console.log(person.name)
console.log(person.age)
console.log(person.gender)
console.log(person.nation)

person.say()

function New() {
  var obj = {}
  Constructor = [].shift.call(arguments) // 獲取arguments第一個(gè)參數(shù):構(gòu)造函數(shù)
  // 注意:此時(shí)的arguments參數(shù)在shift()方法的截取后只剩下兩個(gè)元素
  obj.__proto__ = Constructor.prototype // 把構(gòu)造函數(shù)的原型賦值給obj對(duì)象
  Constructor.apply(obj, arguments) // 改變夠著函數(shù)指針,指向obj,這是剛才上面說到的訪問構(gòu)造函數(shù)里面的屬性和方法的方式
  return obj
}

以上代碼中的New函數(shù),就是new操作符的實(shí)現(xiàn)

主要步驟:

1. 創(chuàng)建一個(gè)空對(duì)象

2. 獲取arguments第一個(gè)參數(shù)

3. 將構(gòu)造函數(shù)的原型鏈賦給obj

4. 使用apply改變構(gòu)造函數(shù)this指向,指向obj對(duì)象,其后,obj就可以訪問到構(gòu)造函數(shù)中的屬性以及原型上的屬性和方法了

5. 返回obj對(duì)象

可能很多小伙伴看到這里覺得new不就是做了這些事情嗎,然而~~

然而我們卻忽略了一點(diǎn),js里面的函數(shù)是有返回值的,即使構(gòu)造函數(shù)也不例外。

如果我們?cè)跇?gòu)造函數(shù)里面返回一個(gè)對(duì)象或一個(gè)基本值,上面的New函數(shù)會(huì)怎樣?

我們?cè)賮砜匆欢未a:

function Person(name, age) {
  this.name = name
  this.age = age
  this.gender = "男"
  
  return {
    name: name,
    gender: "男"
  }
}

Person.prototype.nation = "漢"

Person.prototype.say = function() {
  console.log(`My name is ${this.age}`)
}

var person = new Person("小明", 25)

console.log(person.name)
console.log(person.age)
console.log(person.gender)
console.log(person.nation)

person.say()

執(zhí)行代碼,發(fā)現(xiàn)只有namegender這兩個(gè)字段如期輸出,age、nation為undefined,say()報(bào)錯(cuò)。

改一下代碼構(gòu)造函數(shù)的代碼:

function Person(name, age) {
  this.name = name
  this.age = age
  this.gender = "男"
  
  // return {
  //   name: name,
  //   gender: "男"
  // }
  return 1
}

// ...

執(zhí)行一下代碼,發(fā)現(xiàn)所有字段終于如期輸出。

這里做個(gè)小結(jié):

1. 當(dāng)構(gòu)造函數(shù)返回引用類型時(shí),構(gòu)造里面的屬性不能使用,只能使用返回的對(duì)象;

2. 當(dāng)構(gòu)造函數(shù)返回基本類型時(shí),和沒有返回值的情況相同,構(gòu)造函數(shù)不受影響。

那我們現(xiàn)在來考慮下New函數(shù)要怎么改才能實(shí)現(xiàn)上面總結(jié)的兩點(diǎn)功能呢?繼續(xù)往下看:

function Person(name, age) {
  // ...
}

function New() {
  var obj = {}
  Constructor = [].shift.call(arguments)
  obj.__proto__ = Constructor.prototype
  
  // Constructor.apply(obj, arguments)
  var result = Constructor.apply(obj, arguments)
  
  // return obj
  return typeof result === "object" ? result : obj
}

var person = New(Person, "小明", 25)

console.log(person.name)
// ...

執(zhí)行此代碼,發(fā)現(xiàn)已經(jīng)實(shí)現(xiàn)了上面總結(jié)的兩點(diǎn)。

解決方案:使用變量接收構(gòu)造函數(shù)的返回值,然后在New函數(shù)里面判斷一下返回值類型,根據(jù)不同類型返回不同的值。

看到這里。又有小伙伴說,這下new已經(jīng)完全實(shí)現(xiàn)了吧?!!

答案肯定是否定的。

下面我們繼續(xù)看一段代碼:

function Person(name, age) {
  this.name = name
  this.age = age
  this.gender = "男"
  
  // 返回引用類型
  // return {
  //   name: name,
  //   gender: "男"
  // }
  
  // 返回基本類型
  // return 1
  
  // 例外
  return null
}

再執(zhí)行代碼,發(fā)現(xiàn)又出問題了!!!

又出問題了!為什么……?
...
剛才不是總結(jié)了返回基本類型時(shí)構(gòu)造函數(shù)不受影響嗎,而null就是基本類型啊?
...

解惑:null是基本類型沒錯(cuò),但是使用操作符typeof后我們不難發(fā)現(xiàn):

typeof null === "object" // true

特例:typeof null返回為"object",因?yàn)樘厥庵?b>null被認(rèn)為是一個(gè)空的對(duì)象引用。

明白了這一點(diǎn),那問題就好解決了:

function Person(name, age) {
  // ...
}

function New() {
  var obj = {}
  Constructor = [].shift.call(arguments)
  obj.__proto__ = Constructor.prototype
  // Constructor.apply(obj, arguments)
  var result = Constructor.apply(obj, arguments)
  // return obj
  // return typeof result === "object" ? result : obj
  return typeof result === "object" ? result || obj : obj
}

var person = New(Person, "小明", 25)

console.log(person.name)
// ...

解決方案:判斷一下構(gòu)造函數(shù)返回值result,如果result是一個(gè)引用(引用類型和null),就返回result,但如果此時(shí)result為false(null),就使用操作符||之后的obj

好了,到現(xiàn)在應(yīng)該又有小伙伴發(fā)問了,這下New函數(shù)是徹徹底底實(shí)現(xiàn)了吧!!!

答案是,離完成不遠(yuǎn)了??!

在功能上,New函數(shù)基本完成了,但是在代碼嚴(yán)謹(jǐn)度上,我們還需要做一點(diǎn)工作,繼續(xù)往下看:

這里,我們?cè)谖恼麻_篇做的鋪墊要派上用場(chǎng)了:

var obj = {}

實(shí)際上等價(jià)于

var obj = new Object()

前面說了,以上兩段代碼其實(shí)只是獲取了object對(duì)象的一個(gè)實(shí)例。再者,我們本來就是要實(shí)現(xiàn)new,但是我們?cè)趯?shí)現(xiàn)new的過程中卻使用了new

這個(gè)問題把我們引入到了到底是先有雞還是先有蛋的問題上!

這里,我們就要考慮到ECMAScript底層的API了——Object.create(null)

這句代碼的意思才是真真切切地創(chuàng)建了一個(gè)對(duì)象?。?/p>

function Person(name, age) {
  // ...
}

function New() {
  // var obj = {}
  // var obj = new Object()
  var obj = Object.create(null)
  Constructor = [].shift.call(arguments)
  obj.__proto__ = Constructor.prototype
  // Constructor.apply(obj, arguments)
  var result = Constructor.apply(obj, arguments)
  // return obj
  // return typeof result === "object" ? result : obj
  return typeof result === "object" ? result || obj : obj
}

var person = New(Person, "小明", 25)

console.log(person.name)
console.log(person.age)
console.log(person.gender)
// 這樣改了之后,以下兩句先注釋掉,原因后面再討論
// console.log(person.nation)
// person.say()
好了好了,小伙伴常常舒了一口氣,這樣總算完成了!!
但是,現(xiàn)實(shí)總是殘酷的!

小伙伴:啥?還有完沒完?
function Person(name, age) {
  this.name = name
  this.age = age
  this.gender = "男"
}

Person.prototype.nation = "漢"

Person.prototype.say = function() {
  console.log(`My name is ${this.age}`)
}

function New() {
  // var obj = {}
  // var obj = new Object()
  var obj = Object.create(null)
  Constructor = [].shift.call(arguments)
  obj.__proto__ = Constructor.prototype
  // Constructor.apply(obj, arguments)
  var result = Constructor.apply(obj, arguments)
  // return obj
  // return typeof result === "object" ? result : obj
  return typeof result === "object" ? result || obj : obj
}

var person = New(Person, "小明", 25)

console.log(person.name)
console.log(person.age)
console.log(person.gender)
// 這里解開剛才的注釋
console.log(person.nation)
person.say()

別急,我們執(zhí)行一下修改后的代碼,發(fā)現(xiàn)原型鏈上的屬性nation和方法say()報(bào)錯(cuò),這又是為什么呢?

從上圖我們可以清除地看到,Object.create(null)創(chuàng)建的對(duì)象是沒有原型鏈的,而后兩個(gè)對(duì)象則是擁有__proto__屬性,擁有原型鏈,這也證明了后兩個(gè)對(duì)象是通過繼承得來的。

那既然通過Object.create(null)創(chuàng)建的對(duì)象沒有原型鏈(原型鏈斷了),那我們?cè)趧?chuàng)建對(duì)象的時(shí)候把原型鏈加上不就行了,那怎么加呢?

function Person(name, age) {
  this.name = name
  this.age = age
  this.gender = "男"
}

Person.prototype.nation = "漢"

Person.prototype.say = function() {
  console.log(`My name is ${this.age}`)
}

function New() {
  Constructor = [].shift.call(arguments)
  
  // var obj = {}
  // var obj = new Object()
  // var obj = Object.create(null)
  var obj = Object.create(Constructor.prototype)
  
  // obj.__proto__ = Constructor.prototype
  // Constructor.apply(obj, arguments)
  var result = Constructor.apply(obj, arguments)
  // return obj
  // return typeof result === "object" ? result : obj
  return typeof result === "object" ? result || obj : obj
}

var person = New(Person, "小明", 25)

console.log(person.name)
console.log(person.age)
console.log(person.gender)
console.log(person.nation)

person.say()

這樣創(chuàng)建的對(duì)象就擁有了它初始的原型鏈了,這個(gè)原型鏈?zhǔn)俏覀儌鬟M(jìn)來的構(gòu)造函數(shù)賦予它的。

也就是說,我們?cè)趧?chuàng)建新對(duì)象的時(shí)候,就為它指定了原型鏈了——新創(chuàng)建的對(duì)象繼承自傳進(jìn)來的構(gòu)造函數(shù)!

看到這里,小伙伴們長(zhǎng)長(zhǎng)舒了一口氣,有本事你再給我安排一個(gè)坑出來!

既然都看到這里了,大家要相信我們離最終的曙光已經(jīng)不遠(yuǎn)了!

我想說的是,坑是沒有了,但是為了程序員吹毛求疵的精神!哦不對(duì),是精益求精的精神,我們還有必要啰嗦一點(diǎn)點(diǎn)??!

想必細(xì)心的小伙伴已經(jīng)注意到了,為什么最后一步中的以下代碼:

Constructor = [].shift.call(arguments)
var obj = Object.create(Constructor.prototype)

不能使用以下代碼來代替?

var obj = Object.create(null)
Constructor = [].shift.call(arguments)
obj.__proto__ = Constructor.prototype

換個(gè)方式說,這兩段代碼大致的意思基本相同:都是將構(gòu)造器的原型賦予新創(chuàng)建的對(duì)象。但是為何第二段代碼要報(bào)錯(cuò)(訪問不到原型鏈上的屬性)呢?

這個(gè)問題很吃基本功,認(rèn)真去研究研究js的底層APIObject.create以及原型鏈等知識(shí),就會(huì)明白其中的道理。小伙伴可以拉到文章末尾,我把重點(diǎn)都記錄下來了,以供大家參考。

現(xiàn)在,我們來梳理下最終的New函數(shù)做了什么事,也就是本文討論的結(jié)果——new操作符到底做了什么?

獲取實(shí)參中的第一個(gè)參數(shù)(構(gòu)造函數(shù)),就是調(diào)用New函數(shù)傳進(jìn)來的第一個(gè)參數(shù),暫時(shí)記為Constructor;

使用Constructor的原型鏈結(jié)合Object.create創(chuàng)建一個(gè)對(duì)象,此時(shí)新對(duì)象的原型鏈為Constructor函數(shù)的原型對(duì)象;(結(jié)合我們上面討論的,要訪問原型鏈上面的屬性和方法,要使用實(shí)例對(duì)象的__proto__屬性)

改變Constructor函數(shù)的this指向,指向新創(chuàng)建的實(shí)例對(duì)象,然后call方法再調(diào)用Constructor函數(shù),為新對(duì)象賦予屬性和方法;(結(jié)合我們上面討論的,要訪問構(gòu)造函數(shù)的屬性和方法,要使用call或apply)

返回新創(chuàng)建的對(duì)象,為Constructor函數(shù)的一個(gè)實(shí)例對(duì)象。

現(xiàn)在我,我們來回答文章開始時(shí)提出的問題,new是用來創(chuàng)建對(duì)象的嗎?

現(xiàn)在我們可以勇敢的回答,new是用來做繼承的,而創(chuàng)建對(duì)象的其實(shí)是Object.create(null)。
在new操作符的作用下,我們使用新創(chuàng)建的對(duì)象去繼承了他的構(gòu)造函數(shù)上的屬性和方法、以及他的原型鏈上的屬性和方法!

寫在最后:

補(bǔ)充一點(diǎn)關(guān)于原型鏈的知識(shí):

JavaScript中的函數(shù)也是對(duì)象,而且對(duì)象除了使用字面量定以外,都需要通過函數(shù)來創(chuàng)建對(duì)象;

prototype屬性可以給函數(shù)和對(duì)象添加可共享(繼承)的方法、屬性,而__proto__是查找某函數(shù)或?qū)ο蟮脑玩湻绞剑?/p>

prototype和__proto__都指向原型對(duì)象;

任意一個(gè)函數(shù)(包括構(gòu)造函數(shù))都有一個(gè)prototype屬性,指向該函數(shù)的原型對(duì)象;

任意一個(gè)實(shí)例化的對(duì)象,都有一個(gè)__proto__屬性,指向該實(shí)例化對(duì)象的構(gòu)造函數(shù)的原型對(duì)象。

補(bǔ)充一下關(guān)于Object.create()的知識(shí):

Object.create(null)可以創(chuàng)建一個(gè)沒有原型鏈、真正意義上的空對(duì)象,該對(duì)象不擁有js原生對(duì)象(Object)的任何特性和功能。

就如:即使通過人為賦值的方式(newObj.__proto__ = constructor.prototype)給這個(gè)對(duì)象賦予了原型鏈,
也不能實(shí)現(xiàn)原型鏈逐層查找屬性的功能,因?yàn)檫@個(gè)對(duì)象看起來似乎即使有了"__proto__"屬性,但是它始終沒有直接或間接繼承自O(shè)bject.prototype,
也就不可能擁有js原生對(duì)象(Object)的特性或功能了;

該API配合Object.defineProperty可以創(chuàng)建javascript極其靈活的自定義對(duì)象;

該API是實(shí)現(xiàn)繼承的一種方式;

...

原文:http://blog.xieyangogo.cn/201...

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/103467.html

相關(guān)文章

  • RxJava中作符到底做了什么

    摘要:今年徹底火了一把,其中最牛逼之處就是操作符了,以前只知道怎么用,這幾天看了看源碼,大致的弄清楚了操作符的工作過程,今天分享給大家。如果有什么不對(duì)地方,請(qǐng)大家多多指教。今天我們已為例,看代碼一個(gè)很簡(jiǎn)單的小例子,用過濾操作符找出大于等于的數(shù)字。 ????RxJava今年徹底火了一把,其中最牛逼之處就是操作符了,以前只知道怎么用,這幾天看了看源碼,大致的弄清楚了操作符的工作過程,今天分享給大...

    sunny5541 評(píng)論0 收藏0
  • 在使用es6語法class的時(shí)候,babel到底做了什么?

    摘要:正常情況,的返回值就是一個(gè)對(duì)象,其實(shí)也就是對(duì)象。好了,上面算是基本說清楚了使用語法定義類繼承類,到底發(fā)生了什么,如果錯(cuò)誤,還請(qǐng)指正,謝謝 自從有了webpack之后,我們這些jscoder似乎得到了前所未有的解放,箭頭函數(shù),對(duì)象解構(gòu),let,const關(guān)鍵字,以及class、extends等等關(guān)鍵字使用得不亦樂乎,反正,webpack會(huì)幫我們把這些es6代碼轉(zhuǎn)換成瀏覽器能夠識(shí)別的es5...

    時(shí)飛 評(píng)論0 收藏0
  • 當(dāng)我們?cè)贘avaScript中new一個(gè)對(duì)象的時(shí)候,我們到底在做什么

    摘要:當(dāng)構(gòu)造函數(shù)沒有顯式地返回一個(gè)值的時(shí)候,對(duì)其執(zhí)行操作之后,會(huì)返回這個(gè)構(gòu)造函數(shù)實(shí)例化之后的對(duì)象。 JavaScript里實(shí)例化一個(gè)對(duì)象的時(shí)候,我們常用的方法就是使用new操作符。 var Foo = function(x, y) { this.x = x this.y = y } var foo = new Foo(1, 2) // Foo?{x: 1, y: 2} 那么...

    woshicixide 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<