【寵粉通道】叫我講解的一道前端JS面試題-靈魂三問:prototype?類的prototype是什麼?對象的proto是什麼

file

做者 | Jesksonhtml

來源 | 達達前端小酒館前端

1web

到底是什麼樣的題目讓我徒弟疑惑呢?讓咱們看看截圖先,來源於wx羣:面試

file

看看我是怎麼回答的:算法

file

file

file

file

file

function Foo() {
    getName = function () { alert (1); };
    return this;
}
var getName;
//只提高變量聲明

function getName() { alert (5);}
//提高函數聲明,覆蓋var的聲明

Foo.getName = function () { alert (2);};

Foo.prototype.getName = function () { alert (3);};

getName = function () { alert (4);};
//最終的賦值再次覆蓋function getName聲明

getName();
//最終輸出4

看懂沒呢?沒看懂也別急哈:segmentfault

先讓咱們瞭解函數的定義:網絡

函數聲明,能夠先調用再聲明數據結構

dadaFn();

function dadaFn(){
 console.log(dadaFn);
}

file

/ 返回 f -> function
function dadaFn() {
 console.log(dadaFn);
}

函數表達式,先聲明再調用:

let daDafn = function() {
 console.log("dada");
};

daDafn();

file

let fn = function() {
    console.log(111);
}
fn(); //111
VM1188:2 111
undefined
let daDafn = function() {
 console.log("dada");
}

daDafn();
VM1197:2 dada
undefined

箭頭函數:

let da = () => {};

console.log( da.prototype );

file

JavaScript prototype屬性,可讓你向對象添加屬性和方法:函數

格式:學習

object.prototype.name = value;

使用prototype屬性向對象添加屬性:

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

var dashu = new dada("dashucoding", 13);

dada.prototype.job = null;

dashu.job = it;

console.log(dashu.jog);

輸出結果:

it

構造函數:

let da1 = new Function('a', 'b');

da1(1,2);

file

let da1 = new Function('a', 'b');

da1(1,2);

VM65:3 Uncaught ReferenceError: b is not defined
    at eval (eval at <anonymous> (local-ntp.html:1), <anonymous>:3:1)
    at <anonymous>:3:1
(anonymous) @ VM65:3
(anonymous) @ VM64:3

let fn1 = new Function('a', 'b',  'console.log(a * b)');
fn1(1, 2); //2
VM69:3 2
undefined

預解析,變量提高的概念:

file

面試題

file

function Foo() {
    getName = function () { console.log (1); };
    return this;
}
Foo.getName = function () { console.log (2);};
Foo.prototype.getName = function () { console.log (3);};
var getName = function () { console.log (4);};
function getName() { console.log (5);}

//請寫出如下輸出結果:
Foo.getName();

getName();

Foo().getName();

getName();

new Foo.getName();

new Foo().getName();

new new Foo().getName();

VM243:5 2
VM243:7 4
VM243:2 1
VM243:2 1
VM243:5 2
VM243:6 3
VM243:6 3
//答案:
Foo.getName();//2
getName();//4
Foo().getName();//1
getName();//1
new Foo.getName();//2
new Foo().getName();//3
new new Foo().getName();//3

變量定義提高,this指針指向,運算符優先級

原型、繼承、全局變量污染、對象屬性及原型屬性優先級

靈魂三問:

prototype?類的prototype是什麼?對象的proto是什麼?

在JavaScript中,prototype對象是實現面向對象的一個重要機制。

每一個函數就是一個對象(Function),函數對象都有一個子對象 prototype對象,類是以函數的形式來定義的。prototype表示該函數的原型,也表示一個類的成員的集合。

實現面向對象機制

經過new建立一個類的實例對象的時候,prototype對象的成員都成爲實例化對象的成員。

一、該對象被類所引用,只有函數對象纔可引用;
二、在new實例化後,其成員被實例化,實例對象方可調用。

同時,函數是一個對象,函數對象若直接聲明成員,不用被實例化便可調用。

定義和用法

prototype 屬性使您有能力向對象添加屬性和方法。

構造函數的簡單介紹

file

function Person(){
 this.name = 'dada';
}
 var boy = new Person();
console.log(boy.name); //'dada'
VM254:5 dada
undefined

file

prototype屬性的做用

爲了解決構造函數的對象實例之間沒法共享屬性的缺點,js提供了prototype屬性。

js中每一個數據類型都是對象(除了null和undefined)。

每一個對象都繼承自另一個對象,後者稱爲「原型」(prototype)對象,只有null除外,它沒有本身的原型對象。

原型對象上的全部屬性和方法,都會被對象實例所共享。

經過構造函數生成對象實例時,會將對象實例的原型指向構造函數的prototype屬性。

每個構造函數都有一個prototype屬性,這個屬性就是對象實例的原型對象。

原型對象的屬性不是對象實例的屬性。

prototype是做爲構造函數的屬性

對於對象實例來講,prototype是對象實例的原型對象。

因此prototype便是屬性,又是對象。

file

原型鏈:原型鏈主要用於繼承,每一個對象都有一個proto屬性指向其構造函數的原型對象,當訪問對象的屬性和方法時,會先在對象自身進行查找,若是不存在,則沿着proto屬性向上一級查找,直到沒找到返回 undefined,這樣的查找過程,稱之爲原型鏈。

原型鏈,請找我文章:一篇文章帶你瞭解JavaScript中的變量,做用域,和內存問題。

function Foo() {
    getName = function () { console.log (1); };
    return this;
}
Foo.getName = function () { console.log (2);};
Foo.prototype.getName = function () { console.log (3);};

var getName = function () { console.log (4);};
function getName() { console.log (5);}

//請寫出如下輸出結果:
Foo.getName();  // 2

看代碼,先定義一個Foo()的函數,以後爲Foo建立一個叫getName的靜態屬性,用來存儲一個匿名的函數,以後爲Foo的原型對象,新建立了一個叫getName的匿名函數。

var getName = function () { console.log (4);};
function getName() { console.log (5);}

而後經過函數變量表達式,建立一個getName的匿名函數,而後由經過函數變量表達式,建立一個getName的函數。

最後一個是聲明一個叫getName的函數。

Foo.getName();  // 2

這個不用說就是訪問Foo()函數上存儲的靜態屬性,答爲2。

下一個問:

getName();

Foo().getName();

getName();

new Foo.getName();

new Foo().getName();

new new Foo().getName();
getName(); 的答案呢? // 5

function Foo() {
    getName = function () { console.log (1); };
    return this;
}

這裏要分開理解看

Foo.getName = function () { console.log (2);};
Foo.prototype.getName = function () { console.log (3);};

var getName = function () { console.log (4);};
function getName() { console.log (5);}

預解析/變量提高

file

function Foo() {
    getName = function () { alert (1); };
    return this;
}
var getName;
//只提高變量聲明

function getName() { alert (5);}
//提高函數聲明,覆蓋var的聲明

Foo.getName = function () { alert (2);};
getName = function () { alert (4);};
//最終的賦值再次覆蓋function getName聲明

getName();
//最終輸出4

var getName 只提高變量聲明

function getName 函數聲明,覆蓋var的聲明

第二個答案

直接調用getName()函數,就是訪問當前上文做用域內的叫getName的函數

file

這樣var getName提高,函數總體提高到上面了,那麼就剩下getName結果爲4的那個了。

第三個問是:// 1

一個是變量做用域問題,一個是this指向問題。

Foo().getName();

第一個Foo()函數:

function Foo() {
 getName = function() {
  console.log('1');
 }
}

即Foo().getName()中

file

先執行Foo()函數,而後調用Foo函數返回的返回值對象的getName屬性函數。

file

第四個:

getName(); // 函數 1

至關於window.getName(),由於這個變量被Foo函數執行時修改了,結果一樣爲1。

file

五問:

new Foo.getName();

file

點 . 的優先級高於new操做,等價於:

new (Foo.getName)();
// 將getName函數做爲了構造函數來執行
// 2

六的答案:

file

new Foo().getName(); // 3
// 運算符優先級括號高於new

等價於
(new Foo()).getName()

先執行Foo函數,此時做爲構造函數,有返回值。

file

如有返回值則檢查其返回值是否爲引用類型

file

若返回值是引用類型,則實際返回值爲這個引用類型。

file

價於
(new Foo()).getName()
# ```
![file](https://graph.baidu.com/resource/2224fbd71c4d3b8c9599901577294998.png)

new new Foo().getName();

new ((new Foo()).getName)();

// 3

## ❤️ 不要忘記留下你學習的腳印 [點贊 + 收藏 + 評論]

做者Info:

> 【做者】:Jeskson
> 【原創公衆號】:達達前端小酒館。
> 【福利】:公衆號回覆 「資料」 送自學資料大禮包(進羣分享,想要啥就說哈,看我有沒有)!
> 【轉載說明】:轉載請說明出處,謝謝合做!~

大前端開發,定位前端開發技術棧博客,PHP後臺知識點,web全棧技術領域,數據結構與算法、網絡原理等通俗易懂的呈現給小夥伴。謝謝支持,承蒙厚愛!!!

----
若本號內容有作得不到位的地方(好比:涉及版權或其餘問題),請及時聯繫咱們進行整改便可,會在第一時間進行處理。

----
## 請點贊!由於大家的贊同/鼓勵是我寫做的最大動力!
### 歡迎關注[達達](https://blog.csdn.net/qq_36232611)的CSDN!

**這是一個有質量,有態度的博客**
相關文章
相關標籤/搜索