JavaScript把new給拿捏住了

2022-04-08 22:00:19
本篇文章給大家帶來了關於的相關知識,其中主要介紹了關於new的相關問題,new運運算元建立一個使用者定義的物件型別的範例或具有建構函式的內建物件型別之一,希望對大家有幫助。

【相關推薦:】

前言

什麼是new呢?

new運運算元建立一個使用者定義的物件型別的範例或具有建構函式的內建物件型別之一。

光看定義還是有幾分晦澀,直接看一個具體的例子,來了解一下JavaScript中的new實現的功能。

舉個例子

// 現實中瘦不了,但網路中一定要保持苗條
function Thin_User(name, age) {
    this.name = name;
    this.age = age;
}

Thin_User.prototype.eatToMuch = function () {
    // 白日做夢吧,留下肥胖的淚水
    console.log('i eat so much, but i\'m very thin!!!');
}

Thin_User.prototype.isThin = true;

const xiaobao = new Thin_User('zcxiaobao', 18);
console.log(xiaobao.name);   // zcxiaobao
console.log(xiaobao.age);    // 18
console.log(xiaobao.isThin); // true
// i eat so much, but i'm very thin!!!
xiaobao.eatToMuch();

通過上面這個例子,我們可以發現xiaobao可以:

  • 存取到建構函式Thin_User中屬性
  • 存取到Thin_User.prototype中屬性

描述得更直白一點,new做了這些事:

  • 建立了一個空物件,物件的__proto__->Thin_User.prototype
  • 執行建構函式,並將this指向新物件
  • 返回新物件

補充說明

由於new是關鍵字,我們無法像模擬陣列高階方法一樣覆蓋,因此我們寫一個函數createObject,來模擬new的效果。使用具體如下:

function Thin_User(name, age) {}

const u1 = new Thin_user(...)
const u2 = createObject(Thin_User, ...a\)

初步模擬

根據上面分析,createObject編寫的大致步驟為:

  • 建立一個新物件obj
  • 設定obj.__proto__->constructor.prototype(但JavaScript不推薦直接修改__proto__屬性,提供了setPrototypeOf方法來專門修改原型)
  • 使用constructor.call/apply(obj, ...),將屬性新增到obj
  • 返回obj

__proto__和prototype,可以看JavaScript之徹底理解原型與原型鏈
call/apply,可以看JavaScript之手撕call、apply

學習完這些,我們就可以編寫第一版程式碼:

function createObject(Con) {
    // 建立新物件obj
    // var obj = {};也可以
    var obj = Object.create(null);

    // 將obj.__proto__ -> 建構函式原型
    // (不推薦)obj.__proto__ = Con.prototype
    Object.setPrototypeOf(obj, Con.prototype);

    // 執行建構函式
    Con.apply(obj, [].slice.call(arguments, 1));

    // 返回新物件
    return obj;}

返回值效果

眾所周知,函數是有返回值的,那建構函式如果有返回值,最終執行new後返回的結果是怎樣的那?

返回值為基本型別

假設建構函式返回值為一個基本型別,我們來看一下最後的返回結果:

function Thin_User(name, age) {
    this.name = name;
    this.age = age;
    return 'i will keep thin forever';
}

Thin_User.prototype.eatToMuch = function () {
    console.log('i eat so much, but i\'m very thin!!!');
}

Thin_User.prototype.isThin = true;

const xiaobao = new Thin_User('zcxiaobao', 18);
console.log(xiaobao.name);   // zcxiaobao
console.log(xiaobao.age);    // 18
console.log(xiaobao.isThin); // true
// i eat so much, but i'm very thin!!!
xiaobao.eatToMuch();

最後的返回結果好像受到任何干擾,難道建構函式不會對返回值進行處理嗎?

不急,我們來接著測試一下返回值為物件的情況。

返回值為物件

function Thin_User(name, age) {
    this.name = name;
    this.age = age;
    return {
        name: name,
        age: age * 10,
        fat: true
    }
}

Thin_User.prototype.eatToMuch = function () {
    // 白日做夢吧,留下肥胖的淚水
    console.log('i eat so much, but i\'m very thin!!!');
}

Thin_User.prototype.isThin = true;

const xiaobao = new Thin_User('zcxiaobao', 18);
// Error: xiaobao.eatToMuch is not a function
xiaobao.eatToMuch();

當我執行eatToMuch時,控制檯直接報錯,沒有當前函數,於是我列印了xiaobao物件:
在這裡插入圖片描述

發現xiaobao物件的age發生了改變,而且增加了fat屬性,正好與建構函式的返回值一樣。

看完這兩個例子,基本可以理清建構函式有返回值的情況:當建構函式返回值為物件時,直接返回這個物件。

終版模擬

function createObject(Con) {
    // 建立新物件obj
    // var obj = {};也可以
    var obj = Object.create(null);

    // 將obj.__proto__ -> 建構函式原型
    // (不推薦)obj.__proto__ = Con.prototype
    Object.setPrototypeOf(obj, Con.prototype);

    // 執行建構函式,並接受建構函式返回值
    const ret = Con.apply(obj, [].slice.call(arguments, 1));

    // 若建構函式返回值為物件,直接返回該物件
    // 否則返回obj
    return typeof(ret) === 'object' ? ret: obj;}

【相關推薦:】

以上就是JavaScript把new給拿捏住了的詳細內容,更多請關注TW511.COM其它相關文章!