<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          看完這篇 “原型” & “this”,就兩字“通透了”

          共 19829字,需瀏覽 40分鐘

           ·

          2021-02-28 19:51

          主題

          今天想跟大家分享一個(gè)比較 "別扭" 的概念:“原型 & this” 。

          想把這玩意兒給說(shuō)清楚,大多都會(huì)感到頭大。用的時(shí)候也會(huì)遇到些尷尬的場(chǎng)景。就很難去整明白,這到底是個(gè)啥。

          這一期,就試著將這 說(shuō)個(gè)清楚,講個(gè)明白。開始~

          原型

          什么是 原型 ?帶著這個(gè)問(wèn)題往下看。

          原型-構(gòu)造器 (constructor)

          首先說(shuō)到原型,那就跟對(duì)象密不可分。如果我們需要?jiǎng)?chuàng)建一個(gè)對(duì)象,就需要區(qū)定義一個(gè)object。那我們?cè)陂_發(fā)中如何去創(chuàng)建一個(gè)對(duì)象?肯定有人會(huì)說(shuō),就是var 一個(gè)對(duì)象唄。很好你說(shuō)的很對(duì)~ 確實(shí)是var 一個(gè)對(duì)象,那我如果需要兩個(gè)呢?這個(gè)時(shí)候又會(huì)說(shuō)了,那就var兩個(gè)唄。很好,你又說(shuō)對(duì)了~

          以下是創(chuàng)建對(duì)象的方法。

          code 創(chuàng)建對(duì)象

          var zhangsan = {
           name:'張三',
              age:20
          }

          var lisi = {
           name:'李四',
              age:22
          }


          那如果我們需要?jiǎng)?chuàng)建100個(gè)對(duì)象呢?程序員這么懶,不會(huì)去實(shí)打?qū)嵉恼娴慕o你去 var 100個(gè)對(duì)象。當(dāng)然如果真去這樣做了,里面的變量也是未知的。何況如果是一個(gè)動(dòng)態(tài)創(chuàng)建的,也不能去給代碼寫死不是。

          好了,那這個(gè)時(shí)候,聰明的同學(xué)就已經(jīng)想到了,搞一個(gè) function 函數(shù)唄。專門生成對(duì)象,不就完事拉!

          code 創(chuàng)建對(duì)象

          function User(name, age) {
              var person = {} // 定義一個(gè)person 對(duì)象
              person.name = name; // 往對(duì)象中綁定傳參
              person.age = age;
              return person // 返回生成的新對(duì)象
          }

          var zhangsan = User('張三', 20);
          var lisi = User('李四', 22);


          以上的函數(shù),就會(huì)生成你想要的任何對(duì)象,也稱之為:工廠函數(shù) !一個(gè)專門造對(duì)象的工廠函數(shù)。

          好了,那么這樣做就可以了嗎?是不是發(fā)現(xiàn)了什么?

          對(duì)拉,js中,本身就有一種生產(chǎn)對(duì)象的方式啊,并且更簡(jiǎn)單,不需要再函數(shù)中定義一個(gè)對(duì)象。只需要綁定 this 就可以了。

          code 創(chuàng)建對(duì)象

          function User(name, age) {
            this.name = name; // 這里面的this,就代表了即將生成的那個(gè)對(duì)象 ,并且綁定傳參
            this.age = age;
          }

          var zhangsan = new User('張三', 20);
          var lisi = new User('李四', 22);


          這個(gè)時(shí)候,細(xì)心的同學(xué)已經(jīng)發(fā)現(xiàn)了不同之處。兩個(gè)都是生成對(duì)象的函數(shù),但是叫法就有些不同了。如果是用第二種 js 本身的函數(shù),我們就需要用 new 關(guān)鍵字來(lái)生成對(duì)象。

          code 差異

          var zhangsan =  User('張三', 20); //  第一種
          var zhangsan = new User('張三', 20); //  第二種


          而這種需要用 new 關(guān)鍵字來(lái)叫的函數(shù),稱之為:“構(gòu)造器 constructor or 構(gòu)造函數(shù)”。

          而生成對(duì)象的這個(gè)過(guò)程,稱之為:實(shí)例化。“zhangsan” 可以稱之為一個(gè)對(duì)象,也可以稱之為一個(gè) 實(shí)例

          原型-proto & prototype

          好了,上一段說(shuō)了構(gòu)造器,那么構(gòu)造器是干嘛的?就是造對(duì)象的一個(gè)函數(shù)呀。

          那這一段,來(lái)說(shuō)說(shuō)原型中的重頭戲。先看一段代碼:

          code 創(chuàng)建對(duì)象 在對(duì)象中添加一個(gè)功能屬性,可以引用自己的屬性 "greet"

          function User(name, age) {
            this.name = name; // 這里面的this,就代表了即將生成的那個(gè)對(duì)象 ,并且綁定傳參
            this.age = age;
            this.greet = function () {
              console.log('你好, 我是' + this.name + ',我' + this.age + '歲');
            }
          }

          var zhangsan = new User('張三', 20);
          var lisi = new User('李四', 22);

          zhangsan.greet() // 你好我是張三,我20歲
          lisi.greet() // 你好我是李四,我22歲

          這個(gè)時(shí)候,用生成的對(duì)象來(lái)叫一下 greet 這個(gè)方法,一點(diǎn)毛病沒(méi)有。但是有沒(méi)有同學(xué)發(fā)現(xiàn)什么問(wèn)題?細(xì)心的同學(xué)已經(jīng)發(fā)現(xiàn)了,這兩個(gè)都分別實(shí)例了greet !

          是不是有的同學(xué)有點(diǎn)沒(méi)理解這句話的意思?沒(méi)關(guān)系,接著看:

          code 實(shí)例化后引用 greet 差異對(duì)比

          zhangsan.greet() === lisi.greet()  // false

          同學(xué)們,看到了什么?what? 這兩個(gè)竟然不一樣?

          這意味著什么呢?也就是說(shuō) 張三 和 李四,實(shí)例化之后,都在自己的內(nèi)部,創(chuàng)造了 greet 這樣的屬性。

          這個(gè)時(shí)候,greet 的功能都是一模一樣的呀。如果實(shí)例100個(gè)對(duì)象,豈不是要拷100份?完全沒(méi)必要呀。有沒(méi)有什么方法將這些通用的屬性,放到一個(gè)地方呢?

          有的。接下來(lái)就要說(shuō)到本段的重頭戲之一:prototype 了。在講之前,先看下面一段代碼:

          code 創(chuàng)建對(duì)象 自帶 prototype

          function test1 () {}
          console.log( test1.prototype ) // { constructor : f }

          function test2 () {}
          console.log( test2.prototype ) // { constructor : f }


          發(fā)現(xiàn)了什么?是不是每創(chuàng)建一個(gè)function,都會(huì)自帶一個(gè) prototype 這樣的對(duì)象啊。這就是js 的原生機(jī)制。那為什么 js 的原生機(jī)制 要這么做呢?劃重點(diǎn):prototype 就是給他即將生成的對(duì)象,繼承下去的屬性 看到了什么?prototype 他是一個(gè)屬性,是一個(gè)可供實(shí)例對(duì)象繼承下去的屬性。這不簡(jiǎn)單了嗎。走一個(gè)。

          code 創(chuàng)建對(duì)象 在對(duì)象中添加一個(gè)功能屬性,可以引用自己的屬性 "greet"

          function User(name, age) {
            this.name = name; // 這里面的this,就代表了即將生成的那個(gè)對(duì)象 ,并且綁定傳參
            this.age = age;
          }
          User.prototype.greet = function () {
            console.log('你好, 我是' + this.name + ',我' + this.age + '歲');
          }

          var zhangsan = new User('張三', 20);
          var lisi = new User('李四', 22);

          zhangsan.greet() === lisi.greet()  // true


          既然知道了在構(gòu)造函數(shù)中,使用 prototype 這樣的繼承對(duì)象,可以將 通用 的屬性給 實(shí)例化的對(duì)象繼承下去。

          那么說(shuō)到這,是不是會(huì)有幾個(gè)問(wèn)題?這個(gè)greet 并不是定義在實(shí)例化的對(duì)象里面的啊,來(lái)看一段代碼:

          code prototype

          function User(name, age) {
            this.name = name; // 這里面的this,就代表了即將生成的那個(gè)對(duì)象 ,并且綁定傳參
            this.age = age;
          }
          User.prototype.greet = function () {
            console.log('你好, 我是' + this.name + ',我' + this.age + '歲');
          }
          var lisi = new User('李四', 22);
          console.log(lisi);
            /*
            User {}
            name:'李四'
            age = 22
            __proto__
            greet:f()
            constructor : f User (name, age)
            __proto__:Object
            ...
            */          

          看到了什么?是不是通過(guò) prototype 定義的_greet = function ()_ 屬性跑到了 proto 下面去了。并且,這個(gè)greet屬性雖然沒(méi)有在自己本身的對(duì)象下面,但是一樣可以使用??!我們上面說(shuō)到過(guò):prototype 是繼承屬性對(duì)象。那么看到這里的小伙伴,是不是會(huì)困惑,為什么繼承屬性會(huì)定義在 proto 下面?先別急。接著看!

          這個(gè)時(shí)候已經(jīng)看到了重頭戲之二:proto。再來(lái)看一段代碼:

          code __proto__

          function Test () {}
          Test.prototype.name = 'test'
          var test01 = new Test()
          var test02 = new Test()
          test01.__proto__ === test02.__proto__    // true
          // ----------------------- 實(shí)例之后的對(duì)象調(diào)用__proto__指針指向的 等于被實(shí)例的構(gòu)造函數(shù)的prototype!
          // test01.__proto__ = Test.prototype  // true


          這時(shí)候,是不是已經(jīng)恍然大悟了!原來(lái)通過(guò)prototype 定義的屬性,再被多個(gè)實(shí)例化之后,引用的地址是同一個(gè)!并且 proto 就是我們上面使用的prototype 屬性的馬甲??!就是說(shuō),我們?cè)跇?gòu)造函數(shù)中使用prototype 定義的屬性,都會(huì)被 proto 指針引用!

          好了,這個(gè)時(shí)候,可以整一段比較晦澀的總結(jié)了:每個(gè)對(duì)象都有一個(gè) proto 的屬性,指向該對(duì)象的原型。實(shí)例后通過(guò)對(duì) proto 屬性的訪問(wèn) 去對(duì) prototype對(duì)象進(jìn)行訪問(wèn);原型鏈?zhǔn)怯稍蛯?duì)象組成的,每個(gè)對(duì)象都有__proto__屬性,指向創(chuàng)建該對(duì)象的構(gòu)造函數(shù)的原型 ,然后通過(guò)__proto__屬性將對(duì)象鏈接起來(lái),組成一個(gè)原型鏈,用來(lái)實(shí)現(xiàn)繼承和共享屬性!

          理清楚以上關(guān)系后,可以想一下 通過(guò)prototype 定義的屬性作用就僅僅如此么?接著看一段代碼:

          code prototype

          function Test () {}
          Test.prototype.name = 'test'
          var test01 = new Test()
          console.log( test01.name ) // "test"
          Test.prototype.name = 'no test '
          console.log( test01.name ) // "no test"


          看到了什么?原來(lái) prototype 可以在實(shí)例之后,再進(jìn)行更改呀!

          就是說(shuō),通過(guò)構(gòu)造函數(shù)去改變name 的值,實(shí)例化之后的對(duì)象,引用的屬性值也會(huì)跟著變。太強(qiáng)大了!

          再來(lái)看看 constructor :

          code constructor

          function User(name, age) {
            this.name = name; // 這里面的this,就代表了即將生成的那個(gè)對(duì)象 ,并且綁定傳參
            this.age = age;
          }
          User.prototype.greet = function () {
            console.log('你好, 我是' + this.name + ',我' + this.age + '歲');
          }
          var lisi = new User('李四', 22);

          // 再次構(gòu)造
          var zhangsan = new lisi.constructor('張三', 20) // 使用constructor來(lái)實(shí)例化?。?!
          new lisi.constructor() === new User()  // true
          console.log(zhangsan)
          /*
            User {}
            name:'張三'
            age = 20
            __proto__
            greet:f()
            constructor : f User (name, age)
            __proto__:Object
            ...
            */  


          發(fā)現(xiàn)了嗎?就算我只能知道實(shí)例后的對(duì)象,但是我可以通過(guò) proto 去找到這個(gè)實(shí)例對(duì)象的構(gòu)造函數(shù) constructor ,我再通過(guò)這個(gè)構(gòu)造函數(shù)再去實(shí)例對(duì)象。(var zhangsan = new lisi.constructor('張三', 20))與我直接var zhangsan = new User('張三', 20)。完全一樣。真的很強(qiáng)大!

          好了,講到這,proto & prototype 也就說(shuō)完了,接下來(lái)再說(shuō)說(shuō) 原生對(duì)象的原型。

          原型-原生對(duì)象的原型

          前面,知道了原型的概念,那就趁熱打鐵,接著看看原生對(duì)象的原型。

          先看一段代碼:

          code 原生對(duì)象

            var a ={}
            console.log(a)
            /*
              {}
              __proto__
              greet:f()
              constructor : f Object()
              ...
              */  


          可以看到,我們var 了一個(gè)新對(duì)象之后,沒(méi)有定義任何屬性,但是也能看到他的構(gòu)造函數(shù):Object()。也就是說(shuō):var a ={} === var a = new Object(),兩者沒(méi)有任何區(qū)別。舉個(gè)例子:

          code 原生對(duì)象

            var a ={}
            var b = new Object()
            console.log(a.constructor === b.constructor ) // true


          可以看到,構(gòu)造函數(shù)完全一樣。

          那么這個(gè)時(shí)候,可能會(huì)有同學(xué)想問(wèn),怎么去創(chuàng)造一個(gè)干凈的對(duì)象呢?里面沒(méi)有任何集成的屬性等。

          當(dāng)然也是可以的。接著看:

          code 原生對(duì)象

            var a = new Object.create(null) // 創(chuàng)建函數(shù)必須傳參,一個(gè)對(duì)象或者是 null ,否則會(huì)報(bào)錯(cuò)!
            console.log( a )
            /*
              no prototies 
              */  


          可以看到,通過(guò) Object.create() 創(chuàng)建的對(duì)象,屬性為空。這個(gè)時(shí)候,肯定會(huì)有同學(xué)有疑問(wèn),你這傳的參數(shù)是 null,那當(dāng)然什么都沒(méi)有了,你傳個(gè)對(duì)象試試。哈哈哈,確實(shí),如果傳對(duì)象的話,那就是定義自己所自帶的原型了。舉個(gè)例子:

          code 原生對(duì)象

            var a = new Object.create({name:juejin,des:"666"}) // 創(chuàng)建函數(shù)必須傳參,一個(gè)對(duì)象或者是 null ,否則會(huì)報(bào)錯(cuò)!
            console.log( a )
            /*
              {}
              __proto__
              name:juejin
              des:"666"
                __proto__
                constructor : f Object()
                ...
              */   


          可以看到,再Object.create() 中傳入對(duì)象的屬性,是放在第一層的 proto 下面的,也就是中,這是你創(chuàng)建的這個(gè)原型對(duì)象的繼承屬性,意味著,可以根據(jù)自身的業(yè)務(wù)需求,來(lái)定義自己的原型對(duì)象!

          多級(jí)繼承鏈

          好了,上面已經(jīng)詳細(xì)的講解了原型鏈,構(gòu)造函數(shù),那么就試著來(lái)實(shí)現(xiàn)一個(gè)繼承鏈。看下面代碼:

          code 繼承鏈 從祖父 到爺爺 到爸爸 到自己

          // Animal --> Mammal --> Person --> me
          // Animal 
          function Animal(color, weight) {
            this.color = color;
            this.weight = weight;
          }
          Animal.prototype.eat = function () {
            console.log('吃飯');
          }

          Animal.prototype.sleep = function () {
            console.log('睡覺(jué)');
          }
           //  Mammal
          function Mammal(color, weight) {
            Animal.call(this, color, weight); //綁定 this 這個(gè)下面講
          }

          Mammal.prototype = Object.create(Animal.prototype);
          Mammal.prototype.constructor = Mammal;
          Mammal.prototype.suckle = function () {
            console.log('喝牛奶');
          }
          //  Person
          function Person(color, weight) {
            Mammal.call(this, color, weight);
          }

          Person.prototype = Object.create(Mammal.prototype);
          Person.prototype.constructor = Person;
          Person.prototype.lie = function () {
            console.log('你是個(gè)騙子');
          }
          // 實(shí)例
          var zhangsan = new Person('brown', 100);
          var lisi = new Person('brown', 80);
          console.log('zhangsan:', zhangsan);
          console.log('lisi:', lisi);  
              


          上面的代碼中,實(shí)現(xiàn)了三級(jí)繼承。其中,使用了我們上面講到的 prototype 以及Object.create() 。

          code

          function Animal(color, weight) {
            this.color = color;
            this.weight = weight;
          }
          Animal.prototype.eat = function () {
            console.log('吃飯');
          }


          往祖父類中寫入繼承屬性,eat 供爺爺輩來(lái)繼承這個(gè)吃的屬性。

          code

          //  Mammal
            function Mammal(color, weight) {
              Animal.call(this, color, weight); //綁定 this 這個(gè)下面講
            }
          Mammal.prototype = Object.create(Animal.prototype);
          Mammal.prototype.constructor = Mammal;
          Mammal.prototype.suckle = function () {
            console.log('喝牛奶');
          }


          同時(shí),爺爺輩的屬性,需要繼承祖父輩的其他屬性,因?yàn)樯厦嬗兄v到:prototype 是繼承屬性,也可以稱之為隱性屬性。那么 color, weight 這些顯性屬性怎么給他繼承過(guò)來(lái)呢?

          這個(gè)時(shí)候就用上了上面的 Mammal.prototype = Object.create(Animal.prototype); 這就是利用 Object.create() 來(lái)將祖父的其他顯性屬性,全部繼承到爺爺輩。并且再寫進(jìn)爺爺輩的prototype 中,方便再往下給爸爸繼承。

          這樣一級(jí)一級(jí)的綁定,構(gòu)建,就實(shí)現(xiàn)了所謂的 多級(jí)繼承 了。

          當(dāng)然細(xì)心的同學(xué)又發(fā)現(xiàn)了一個(gè)點(diǎn):

          code

           //  Mammal
          function Mammal(color, weight) {
            Animal.call(this, color, weight); //綁定 this 
          }


          為什么這邊的爺爺輩的構(gòu)造器里面為什么要 call this 呢? ,這邊就先賣個(gè)關(guān)子,下面this那段會(huì)講到!嘿嘿~

          原型總結(jié)

          好了,講了這么多,終于說(shuō)完了原型鏈。其實(shí)一圖勝千言。

          引用上面的一句話:每個(gè)對(duì)象都有一個(gè) proto 的屬性,指向該對(duì)象的原型。實(shí)例后通過(guò)對(duì)proto 屬性的訪問(wèn) 去對(duì) prototype對(duì)象進(jìn)行訪問(wèn);原型鏈?zhǔn)怯稍蛯?duì)象組成的,每個(gè)對(duì)象都有__proto__屬性,指向創(chuàng)建該對(duì)象的構(gòu)造函數(shù)的原型 ,然后通過(guò)__proto__屬性將對(duì)象鏈接起來(lái),組成一個(gè)原型鏈,用來(lái)實(shí)現(xiàn)繼承和共享屬性!

          說(shuō)到這,原型鏈也就說(shuō)完了,接下來(lái)再啃一塊硬骨頭:this。

          this

          其實(shí)說(shuō)到 this,大家都有這樣的一個(gè)感覺(jué),就是一看就會(huì),一用就亂。那么這個(gè)this 到底是個(gè)啥?能不能給它整明白?別急,

          先來(lái)看一段代碼:

          code

          var User = {
           fname:'三',
               lname:'張'
               fullname:function(){
                return User.lname + User.fname
               }
            }
           console.log(User.fullname) // "張三"


          這段代碼是去獲取 User 對(duì)象下的全名,可以看到是沒(méi)什么問(wèn)題。那么這個(gè)時(shí)候,需要給這個(gè)對(duì)象換成person對(duì)象,會(huì)發(fā)生什么呢?

          code

          var Person = {
           fname:'三',
               lname:'張'
               fullname:function(){
                return User.lname + User.fname
               }
            }
           console.log(Person.fullname) // User is not defined


          看到了什么,找不到這個(gè) User,這是為什么呢?很明顯,是因?yàn)槲覀冊(cè)?strong style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;">return 中,返回的還是User 這個(gè)對(duì)象,但是這個(gè)時(shí)候,我已經(jīng)將原來(lái)的 User 改成 Person 了。所以,如果這段代碼想生效,必須也要將 return 中的 User 對(duì)象 改成 Person 對(duì)象。

          麻不麻煩?可重用性也太低了。那么這個(gè)時(shí)候,this 就派上用場(chǎng)了。接著看:

          code

          var Person = {
           fname:'三'
               lname:'張',
               fullname:function(){
                return this.lname + this.fname
               }
            }
           console.log(Person.fullname) // "張三"


          這時(shí)候,就能看到,我對(duì)象名改成了Person,是一樣可以拿到這個(gè)對(duì)象下的 fullname

          是不是有同學(xué)會(huì)問(wèn)了,這是為什么?其實(shí)這個(gè)時(shí)候,這里面的this,就指向了這個(gè)fullname的 fnc 外的Person對(duì)象了。是不是覺(jué)得說(shuō)的有點(diǎn)干,那我們就來(lái)看看:

          code

          var Person = {
           fname:'三',
               lname:'張',
               fullname:function(){
                   console.log(this) // 在哪邊引用this,就在哪邊看!
                return this.lname + this.fname
               }
            }
          /*
          fname:'三'
          lname:'張'
          fullname:f()
          __proto__
                constructor : f Object()
                ...
          */


          這樣看,是不是十分清晰明了。其實(shí)也就是說(shuō),我在 fullname 這個(gè)方法中使用的 this 就是指向了,我當(dāng)前這個(gè) function 代碼塊的上一級(jí)。

          看到這,是不是感覺(jué)明白了?再來(lái):

          code

          var Person = {
           fname:'三',
               lname:'張',
               fullname:function(){
                return this.lname + this.fname
               }
            }
          var  getfullname = Person.fullname // 將Person對(duì)象中的fullname 方法,給到新定義的參數(shù)使用
          console.log(getfullname()) // NAN


          這是什么?沒(méi)拿到 張三 ?這是為啥?

          到這里是不是一下子又懵了?這個(gè) this 到底有多少幺蛾子。打印出來(lái)看看,這個(gè)時(shí)候的 this到底是什么:

          code

          var Person = {
           fname:'三'
               lname:'張',
               fullname:function(){
                   console.log(this) 
                return this.lname + this.fname
               }
            }
          var  getfullname = Person.fullname // 將Person對(duì)象中的fullname 方法,給到新定義的參數(shù)使用
          console.log(getfullname()) // window:{},NAN


          看到什么了?這個(gè) this 竟然指向了window,全局變量。這是咋回事?這就是 this 坑的地方,我上面說(shuō)到:this 就是指向了,我當(dāng)前這個(gè) function 代碼塊的上一級(jí)。其實(shí)這句話,在這邊就直接錯(cuò)了。因?yàn)閠his引用沒(méi)變。只是我的調(diào)用方式變了。

          所以這個(gè)時(shí)候,這句話要重新描述,謹(jǐn)記:this 并不取決于它所在的位置,而是取決于它所在的function是怎么被調(diào)用的?。。?/strong>

          而上面 console.log(Person.fullname) // "張三" 可以打印出結(jié)果,就是fullname的這個(gè)方法,直接被它的父級(jí)調(diào)用了,也就是說(shuō)這個(gè)時(shí)候的 this 是指向的 Person

          而如果指定調(diào)用這個(gè) this 的,并不是直接父級(jí),那么再非嚴(yán)格模式下,指向的就是全局window,而在嚴(yán)格模式下則是 undefined

          再來(lái) 如果 this 再構(gòu)造函數(shù)中被調(diào)用,會(huì)是怎么樣?看下面一段代碼 :

          code

          function User (){
           console.log(this)
          }
          User () // undefined 
          new User () // User {}


          這個(gè)時(shí)候,可以看到,如果 this 是放在構(gòu)造函數(shù)中,被直接調(diào)用 User (),那么這個(gè)時(shí)候的this 就是 undefined 。因?yàn)?nbsp;this 所在的 function 并沒(méi)有作為一個(gè)方法被調(diào)用。

          而 如果是通過(guò) new 的方式被調(diào)用的,那么這個(gè)時(shí)候, this 所在的 function 就被調(diào)用了,并且指向的就是被調(diào)用的 User {} 。還記得我們上面說(shuō)的,js 本身的構(gòu)造函數(shù)機(jī)制嗎?再來(lái)復(fù)習(xí)一下:

          code 創(chuàng)建對(duì)象 "

          function User(name, age) {
            this.name = name; // 這里面的this,就代表了即將生成的那個(gè)對(duì)象 ,并且綁定傳參
            this.age = age;
          }


          就是說(shuō):構(gòu)造函數(shù)中的 this ,就是指向即將實(shí)例化的那個(gè)對(duì)象。謹(jǐn)記!

          所以 總結(jié)一下 this 的三種場(chǎng)景:

          1. 如果this 是 在一個(gè)函數(shù)中,并且被用作方法來(lái)叫,那么這個(gè)時(shí)候的 this 就指向了父級(jí)對(duì)象;
          2. 如果this 是在匿名函數(shù),或者全局環(huán)境的函數(shù)中,那么這個(gè)時(shí)候的 this 就是;undefined;
          3. 如果this 是在構(gòu)造函數(shù)中,那么這個(gè)時(shí)候的 this 就指向了即將生成的那個(gè)對(duì)象


          好了,既然區(qū)分了 this 的使用場(chǎng)景之后,那么它的強(qiáng)大之處是什么呢?舉個(gè)例子:

          code 動(dòng)態(tài)綁定 this

          function introduction() {
            console.log('你好, 我是' + this.name);
          }

          var zhangsan = {
            name: '張三',
          }

          var lisi = {
            name: '李四',
          }

          zhangsan.introduction = introduction;
          lisi.introduction = introduction;

          zhangsan.introduction(); //  你好,我是張三
          lisi.introduction();  //  你好,我是李四


          上面可以看到,定義了一個(gè)方法,這個(gè)方法中使用了 this.name ,但是這個(gè)時(shí)候,并不知道,這個(gè)方法中的 this 到底指向的是誰(shuí),而是等待著誰(shuí)來(lái)調(diào)用它?;貞浺幌律厦嬲f(shuō)的那句話:this 并不取決于它所在的位置,而是取決于它所在的function是怎么被調(diào)用的?。?!

          而這個(gè)時(shí)候,定義了 張三 和 李四 兩個(gè)對(duì)象,這兩個(gè)對(duì)象,分別將定義的 introduction 賦值到本身的對(duì)象下面,也就是說(shuō),這個(gè)時(shí)候, 張三 和 李四 兩個(gè)對(duì)象,都擁有了 introduction這個(gè)方法,并且調(diào)用了。所以,這個(gè)時(shí)候的 function introduction() 已經(jīng)擁有了被調(diào)用的對(duì)象,所以其中的 this.name 也就分別指向了這兩個(gè)對(duì)象的中name。

          好,以上就是將 this 的默認(rèn)指向講完了。但是是不是有個(gè)問(wèn)題,還沒(méi)解決?

          那就是我們之前在說(shuō) 多級(jí)繼承 的時(shí)候,有個(gè) call this 。這個(gè)賣的關(guān)子 還沒(méi)說(shuō)呢?那接下來(lái)就講講。關(guān)于 this 改變它的默認(rèn)指向,綁定一個(gè)我想要綁定的環(huán)境,行不行?

          bind & apply & call

          好了,這一段,就接著上面的講,這里會(huì)講到關(guān)于 this 的三種綁定方法。先來(lái)看代碼:

          code 動(dòng)態(tài)綁定 this

          function introduction() {
            console.log('你好, 我是' + this.name);
          }
          introduction() // 你好, 我是 undefined


          這個(gè)結(jié)果相信大家不會(huì)陌生,因?yàn)榫褪巧厦嬷v的第二種情況:2. 如果this 是在匿名函數(shù),或者全局環(huán)境的函數(shù)中,那么這個(gè)時(shí)候的 this 就是;undefined。

          這里普及一個(gè)知識(shí):introduction() === introduction.call() 只是前者是后者的簡(jiǎn)寫!并且call()中的第一個(gè)傳參可以指定這個(gè)函數(shù)中的 this 指向誰(shuí)!

          好了,知道這個(gè)知識(shí)點(diǎn),再看下面的代碼:

          code 動(dòng)態(tài)綁定 this

          function introduction() {
            console.log('你好, 我是' + this.name);
          }
          var zhangsan = {
           name:'張三'
          }
          introduction.call(zhangsan) // 你好, 我是 張三


          看完是不是一目了然,這個(gè)call()里面?zhèn)鞯膮?shù),指向了 zhangsan 這個(gè)對(duì)象。那這不就是給這個(gè) introduction 方法指定了調(diào)用的父級(jí)了嗎?this 也就指向給調(diào)用這個(gè)方法的 zhangsan了呀!

          說(shuō)到這是不是就能清楚的知道,這個(gè)跟上面 在對(duì)象中,來(lái)綁定這個(gè)方法,來(lái)關(guān)聯(lián)父級(jí)調(diào)用關(guān)系,是一樣的。一個(gè)是對(duì)象引用方法,這個(gè)就是方法綁定對(duì)象呀!

          好,再來(lái):

          code 動(dòng)態(tài)綁定 this

          function introduction(name) {
            console.log('你好,'+ name +' 我是' + this.name);
          }
          var zhangsan = {
           name:'張三'
          }
          introduction.call(zhangsan,"李四") // 你好 李四, 我是 張三


          可以看到call() 除了可以指定this指向的對(duì)象,還可以傳一些其他的參數(shù)。

          好了,說(shuō)到這,是不是已經(jīng)能猜到:bind & apply 怎么用拉!

          大同小異:

          code 動(dòng)態(tài)綁定 this

          function introduction(name) {
            console.log('你好,'+ name +' 我是' + this.name);
          }
          var zhangsan = {
           name:'張三'
          }  
          introduction.call(zhangsan,"李四")   // 你好 李四, 我是 張三   call
          introduction.apply(zhangsan,["李四"])   // 你好 李四, 我是 張三   apply
          intro = introduction.bind(zhangsan)
          intro("李四")// 你好 李四, 我是 張三   bind


          可以看到,call() 和 apply() 區(qū)別就在于,后面的傳參的格式是:數(shù)組的形式;

          而 bind() 則是返回一個(gè)綁定新環(huán)境的 function,等著被調(diào)用。

          結(jié)語(yǔ)

          好啦,這期關(guān)于 “原型” & “this” 的內(nèi)容就全部說(shuō)完了,看到這,就兩個(gè)字:“透徹”。

          源自:https://juejin.cn/post/6921686794987634695

          聲明:文章著作權(quán)歸作者所有,如有侵權(quán),請(qǐng)聯(lián)系小編刪除。

          感謝 · 轉(zhuǎn)發(fā)歡迎大家留言

          關(guān)注數(shù):10億+ 文章數(shù):10億+
          粉絲量:10億+ 點(diǎn)擊量:10億+

           


          微信群管理員請(qǐng)掃描這里

          微信群管理員請(qǐng)掃描這里

          喜歡本文的朋友,歡迎關(guān)注公眾號(hào) 程序員哆啦A夢(mèng),收看更多精彩內(nèi)容

          點(diǎn)個(gè)[在看],是對(duì)小達(dá)最大的支持!


          如果覺(jué)得這篇文章還不錯(cuò),來(lái)個(gè)【分享、點(diǎn)贊、在看】三連吧,讓更多的人也看到

          瀏覽 31
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  蜜桃91精品入口 | 日韩一级色情片 | 黄色网页在线视频 | 国产精品久久久久久久久久乐趣播 | 成人aV无码精品国产一区二区 |