這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 1. 代碼輸出結果 function Person(name) { this.name = name } var p2 = new Person('king'); console.log(p2.__proto__) //Person.pr ...
這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助
1. 代碼輸出結果
function Person(name) { this.name = name } var p2 = new Person('king'); console.log(p2.__proto__) //Person.prototype console.log(p2.__proto__.__proto__) //Object.prototype console.log(p2.__proto__.__proto__.__proto__) // null console.log(p2.__proto__.__proto__.__proto__.__proto__)//null後面沒有了,報錯 console.log(p2.__proto__.__proto__.__proto__.__proto__.__proto__)//null後面沒有了,報錯 console.log(p2.constructor)//Person console.log(p2.prototype)//undefined p2是實例,沒有prototype屬性 console.log(Person.constructor)//Function 一個空函數 console.log(Person.prototype)//列印出Person.prototype這個對象里所有的方法和屬性 console.log(Person.prototype.constructor)//Person console.log(Person.prototype.__proto__)// Object.prototype console.log(Person.__proto__) //Function.prototype console.log(Function.prototype.__proto__)//Object.prototype console.log(Function.__proto__)//Function.prototype console.log(Object.__proto__)//Function.prototype console.log(Object.prototype.__proto__)//null
這道義題目考察原型、原型鏈的基礎,記住就可以了。
2. 代碼輸出結果
// a function Foo () { getName = function () { console.log(1); } return this; } // b Foo.getName = function () { console.log(2); } // c Foo.prototype.getName = function () { console.log(3); } // d var getName = function () { console.log(4); } // e function getName () { console.log(5); } Foo.getName(); // 2 getName(); // 4 Foo().getName(); // 1 getName(); // 1 new Foo.getName(); // 2 new Foo().getName(); // 3 new new Foo().getName(); // 3
輸出結果:2 4 1 1 2 3 3
解析:
- Foo.getName(),Foo為一個函數對象,對象都可以有屬性,b 處定義Foo的getName屬性為函數,輸出2;
- getName(),這裡看d、e處,d為函數表達式,e為函數聲明,兩者區別在於變數提升,函數聲明的 5 會被後邊函數表達式的 4 覆蓋;
- Foo().getName(),這裡要看a處,在Foo內部將全局的getName重新賦值為 console.log(1) 的函數,執行Foo()返回 this,這個this指向window,Foo().getName() 即為window.getName(),輸出 1;
- getName(),上面3中,全局的getName已經被重新賦值,所以這裡依然輸出 1;
- new Foo.getName(),這裡等價於 new (Foo.getName()),先執行 Foo.getName(),輸出 2,然後new一個實例;
- new Foo().getName(),這裡等價於 (new Foo()).getName(), 先new一個Foo的實例,再執行這個實例的getName方法,但是這個實例本身沒有這個方法,所以去原型鏈__protot__上邊找,實例.__protot__ === Foo.prototype,所以輸出 3;
- new new Foo().getName(),這裡等價於new (new Foo().getName()),如上述6,先輸出 3,然後new 一個 new Foo().getName() 的實例。
3. 代碼輸出結果
var F = function() {}; Object.prototype.a = function() { console.log('a'); }; Function.prototype.b = function() { console.log('b'); } var f = new F(); f.a(); f.b(); F.a(); F.b()
輸出結果:
a Uncaught TypeError: f.b is not a function a b
解析:
- f 並不是 Function 的實例,因為它本來就不是構造函數,調用的是 Function 原型鏈上的相關屬性和方法,只能訪問到 Object 原型鏈。所以 f.a() 輸出 a ,而 f.b() 就報錯了。
- F 是個構造函數,而 F 是構造函數 Function 的一個實例。因為 F instanceof Object === true,F instanceof Function === true,由此可以得出結論:F 是 Object 和 Function 兩個的實例,即 F 能訪問到 a, 也能訪問到 b。所以 F.a() 輸出 a ,F.b() 輸出 b。
4. 代碼輸出結果
function Foo(){ Foo.a = function(){ console.log(1); } this.a = function(){ console.log(2) } } Foo.prototype.a = function(){ console.log(3); } Foo.a = function(){ console.log(4); } Foo.a(); let obj = new Foo(); obj.a(); Foo.a();
輸出結果:4 2 1
解析:
- Foo.a() 這個是調用 Foo 函數的靜態方法 a,雖然 Foo 中有優先順序更高的屬性方法 a,但 Foo 此時沒有被調用,所以此時輸出 Foo 的靜態方法 a 的結果:4
- let obj = new Foo(); 使用了 new 方法調用了函數,返回了函數實例對象,此時 Foo 函數內部的屬性方法初始化,原型鏈建立。
- obj.a() ; 調用 obj 實例上的方法 a,該實例上目前有兩個 a 方法:一個是內部屬性方法,另一個是原型上的方法。當這兩者都存在時,首先查找 ownProperty ,如果沒有才去原型鏈上找,所以調用實例上的 a 輸出:2
- Foo.a() ; 根據第2步可知 Foo 函數內部的屬性方法已初始化,覆蓋了同名的靜態方法,所以輸出:1
5. 代碼輸出結果
function Dog() { this.name = 'puppy' } Dog.prototype.bark = () => { console.log('woof!woof!') } const dog = new Dog() console.log(Dog.prototype.constructor === Dog && dog.constructor === Dog && dog instanceof Dog)
輸出結果:true
解析:
因為constructor是prototype上的屬性,所以dog.constructor實際上就是指向Dog.prototype.constructor;constructor屬性指向構造函數。instanceof而實際檢測的是類型是否在實例的原型鏈上。
constructor是prototype上的屬性,這一點很容易被忽略掉。constructor和instanceof 的作用是不同的,感性地來說,constructor的限制比較嚴格,它只能嚴格對比對象的構造函數是不是指定的值;而instanceof比較鬆散,只要檢測的類型在原型鏈上,就會返回true。
6. 代碼輸出結果
var A = {n: 4399}; var B = function(){this.n = 9999}; var C = function(){var n = 8888}; B.prototype = A; C.prototype = A; var b = new B(); var c = new C(); A.n++ console.log(b.n); console.log(c.n);
輸出結果:9999 4400
解析:
- console.log(b.n),在查找b.n是首先查找 b 對象自身有沒有 n 屬性,如果沒有會去原型(prototype)上查找,當執行var b = new B()時,函數內部this.n=9999(此時this指向 b) 返回b對象,b對象有自身的n屬性,所以返回 9999。
- console.log(c.n),同理,當執行var c = new C()時,c對象沒有自身的n屬性,向上查找,找到原型 (prototype)上的 n 屬性,因為 A.n++(此時對象A中的n為4400), 所以返回4400。
7. 代碼輸出問題
function A(){ } function B(a){ this.a = a; } function C(a){ if(a){ this.a = a; } } A.prototype.a = 1; B.prototype.a = 1; C.prototype.a = 1; console.log(new A().a); console.log(new B().a); console.log(new C(2).a);
輸出結果:1 undefined 2
解析:
- console.log(new A().a),new A()為構造函數創建的對象,本身沒有a屬性,所以向它的原型去找,發現原型的a屬性的屬性值為1,故該輸出值為1;
- console.log(new B().a),ew B()為構造函數創建的對象,該構造函數有參數a,但該對象沒有傳參,故該輸出值為undefined;
- console.log(new C(2).a),new C()為構造函數創建的對象,該構造函數有參數a,且傳的實參為2,執行函數內部,發現if為真,執行this.a = 2,故屬性a的值為2。
8 代碼輸出問題
function Parent() { this.a = 1; this.b = [1, 2, this.a]; this.c = { demo: 5 }; this.show = function () { console.log(this.a , this.b , this.c.demo ); } } function Child() { this.a = 2; this.change = function () { this.b.push(this.a); this.a = this.b.length; this.c.demo = this.a++; } } Child.prototype = new Parent(); var parent = new Parent(); var child1 = new Child(); var child2 = new Child(); child1.a = 11; child2.a = 12; parent.show(); child1.show(); child2.show(); child1.change(); child2.change(); parent.show(); child1.show(); child2.show();
輸出結果:
parent.show(); // 1 [1,2,1] 5 child1.show(); // 11 [1,2,1] 5 child2.show(); // 12 [1,2,1] 5 parent.show(); // 1 [1,2,1] 5 child1.show(); // 5 [1,2,1,11,12] 5 child2.show(); // 6 [1,2,1,11,12] 5
這道題目值得神帝,他涉及到的知識點很多,例如this的指向、原型、原型鏈、類的繼承、數據類型等。
解析:
- parent.show(),可以直接獲得所需的值,沒啥好說的;
- child1.show(),
Child
的構造函數原本是指向Child
的,題目顯式將Child
類的原型對象指向了Parent
類的一個實例,需要註意Child.prototype
指向的是Parent
的實例parent
,而不是指向Parent
這個類。 - child2.show(),這個也沒啥好說的;
- parent.show(),
parent
是一個Parent
類的實例,Child.prorotype
指向的是Parent
類的另一個實例,兩者在堆記憶體中互不影響,所以上述操作不影響parent
實例,所以輸出結果不變; - child1.show(),
child1
執行了change()
方法後,發生了怎樣的變化呢?
- this.b.push(this.a),由於this的動態指向特性,this.b會指向
Child.prototype
上的b數組,this.a會指向child1
的a屬性,所以Child.prototype.b
變成了[1,2,1,11]; - this.a = this.b.length,這條語句中
this.a
和this.b
的指向與上一句一致,故結果為child1.a
變為4; - this.c.demo = this.a++,由於
child1
自身屬性並沒有c這個屬性,所以此處的this.c
會指向Child.prototype.c
,this.a
值為4,為原始類型,故賦值操作時會直接賦值,Child.prototype.c.demo
的結果為4,而this.a
隨後自增為5(4 + 1 = 5)。
child2
執行了change()
方法, 而child2
和child1
均是Child
類的實例,所以他們的原型鏈指向同一個原型對象Child.prototype
,也就是同一個parent
實例,所以child2.change()
中所有影響到原型對象的語句都會影響child1
的最終輸出結果。
- this.b.push(this.a),由於this的動態指向特性,this.b會指向
Child.prototype
上的b數組,this.a會指向child2
的a屬性,所以Child.prototype.b
變成了[1,2,1,11,12]; - this.a = this.b.length,這條語句中
this.a
和this.b
的指向與上一句一致,故結果為child2.a
變為5; - this.c.demo = this.a++,由於
child2
自身屬性並沒有c這個屬性,所以此處的this.c
會指向Child.prototype.c
,故執行結果為Child.prototype.c.demo
的值變為child2.a
的值5,而child2.a
最終自增為6(5 + 1 = 6)。
9. 代碼輸出結果
function SuperType(){ this.property = true; } SuperType.prototype.getSuperValue = function(){ return this.property; }; function SubType(){ this.subproperty = false; } SubType.prototype = new SuperType(); SubType.prototype.getSubValue = function (){ return this.subproperty; }; var instance = new SubType(); console.log(instance.getSuperValue());
輸出結果:true
實際上,這段代碼就是在實現原型鏈繼承,SubType繼承了SuperType,本質是重寫了SubType的原型對象,代之以一個新類型的實例。SubType的原型被重寫了,所以instance.constructor指向的是SuperType。具體如下: