一、this關鍵字
作爲函數被調用
函數也可以直接被調用,此時 this 綁定到全局對象。在瀏覽器中,window 就是該全局對象。比如下面的例子:函數被調用時,this 被綁定到全局對象,接下來執行賦值語句,相當於隱式的聲明瞭一個全局變量,這顯然不是調用者希望的。
function makeNoSense(x) {
this.x = x;
}
makeNoSense(5);
x;// x 已經成爲一個值爲 5 的全局變量
作爲對象方法調用
在 JavaScript 中,函數也是對象,因此函數可以作爲一個對象的屬性,此時該函數被稱爲該對象的方法,在使用這種調用方式時,this 被自然綁定到該對象。
var point = {
x : 0,
y : 0,
moveTo : function(x, y) {
this.x = this.x + x;
this.y = this.y + y;
}
};
point.moveTo(1, 1)//this 綁定到當前對象,即 point 對象
內部函數再定義內部函數
上述的moveTo()函數已經是point的內部函數了,如果,我還想在moveTo中再加入一個函數:
var point = {
x : 0,
y : 0,
moveTo : function(x, y) {
console.log(this); //point對象
// 內部函數
var moveX = function(x) {
console.log(this); //window對象
this.x = x;//this 綁定到了哪裏?
};
// 內部函數
var moveY = function(y) {
this.y = y;//this 綁定到了哪裏?
};
moveX(x);
moveY(y);
}
};
point.moveTo(1, 1);
point.x; //==>0
point.y; //==>0
x; //==>1
y; //==>1
這裏出現了一個錯誤,第一個this顯示出是point對象,但是第二個this卻顯示出是window對象。
這屬於 JavaScript 的設計缺陷,正確的設計方式是內部函數的 this 應該綁定到其外層函數對應的對象上,爲了規避這一設計缺陷,聰明的 JavaScript 程序員想出了變量替代的方法,約定俗成,該變量一般被命名爲 that。
var point = {
x : 0,
y : 0,
moveTo : function(x, y) {
var that = this;
// 內部函數
var moveX = function(x) {
that.x = x;
};
// 內部函數
var moveY = function(y) {
that.y = y;
}
moveX(x);
moveY(y);
}
};
point.moveTo(1, 1);
point.x; //==>1
point.y; //==>1
當內部函數嵌套只有一層,那麼還是符合內部函數this綁定在外層函數對應的對象上
,但是,如果超過了1層嵌套,那麼在高於1層的內部函數中,this對象如果不借助that,那麼this將會指向window對象。
apply或call調用
function Point(x, y){
this.x = x;
this.y = y;
this.moveTo = function(x, y){
this.x = x;
this.y = y;
}
}
var p1 = new Point(0, 0);
var p2 = {x: 0, y: 0};
p1.moveTo(1, 1);
p1.moveTo.apply(p2, [10, 10]);
這裏p1是一個Point {x: 1, y: 1}
對象,而p2則是一個Object {x: 10, y: 10}
對象。
eval調用
JavaScript 中的 eval 方法可以將字符串轉換爲 JavaScript 代碼,使用 eval 方法時,this 指向哪裏呢?答案很簡單,看誰在調用 eval 方法,調用者的執行環境(ExecutionContext)中的 this 就被 eval 方法繼承下來了。
二、proto 和. prototype
p.proto === p.constructor.prototype === Person.prototype;
一、 所有構造器/函數的_proto_都指向Function.prototype,它是一個空函數(Empty function)
function A(){}
A.__proto__ === Function.prototype; //true
//內置對象
Number.__proto__ === Function.prototype // true
特殊的,如Math對象的proto是Object.prototype
Math.__proto__ === Object.prototype // true
知道了所有構造器(含內置及自定義)的proto都是Function.prototype,那Function.prototype的proto是誰呢?
Function.prototype.__proto__ === Object.prototype;//true
這說明所有的構造器也都是一個普通JS對象,可以給構造器添加/刪除屬性等。同時它也繼承了Object.prototype上的所有方法:toString、valueOf、hasOwnProperty等。
最後Object.prototype的proto是誰?
Object.prototype.__proto__ === null // true
二、所有對象的_proto_都指向其構造器的prototype
__proto__
指向了,我不知道怎麼說,用java的話來說就是class類。
var obj = {name: 'jack'}
var arr = [1,2,3]
var reg = /hello/g
var date = new Date
var err = new Error('exception')
console.log(obj.__proto__ === Object.prototype) // true
console.log(arr.__proto__ === Array.prototype) // true
console.log(reg.__proto__ === RegExp.prototype) // true
console.log(date.__proto__ === Date.prototype) // true
console.log(err.__proto__ === Error.prototype) // true
再看看自定義的構造器,這裏定義了一個Person
function Person(name) {
this.name = name
}
var p = new Person('jack')
console.log(p.__proto__ === Person.prototype) // true
p是Person的實例對象,p的內部原型總是指向其構造器Person的prototype。
每個對象都有一個constructor屬性,可以獲取它的構造器,因此以下打印結果也是恆等的
function Person(name) {
this.name = name
}
var p = new Person('jack')
p.__proto__ === p.constructor.prototype
修改原型
function Person(name) {
this.name = name
}
var p = new Person('jack')
Person.prototype = {
getName: function() {}
}
又由於
p.__proto__ === p.constructor.prototype === Person.prototype;
所以,我覺得有三種寫法。
Person.prototype.getName = function(){};
var per = new Person('jack');
per.__proto__.getName = function(){};
per.constructor.prototype.getName = function(){};
三、constructor屬性
返回一個指向創建了該對象原型的函數引用。
var a,b;
(function(){
function A (arg1,arg2) {
this.a = 1;
this.b=2;
}
A.prototype.log = function () {
console.log(this.a);
}
a = new A();
b = new A();
})()
a.log();
// 1
b.log();
// 1
通過以上代碼我們可以得到兩個對象,a,b,他們同爲類A的實例。因爲A在閉包裏,所以現在我們是不能直接訪問A的,那如果我想給類A增加新方法怎麼辦?
// a.constructor.prototype 在chrome,firefox中可以通過 a.__proto__ 直接訪問
a.constructor.prototype.log2 = function () {
console.log(this.b)
}
a.log2();
// 2
b.log2();
// 2
由於閉包的特性,我們只能藉助a.constructor.prototype
來更改原型。
參考
https://www.ibm.com/developerworks/cn/web/1207_wangqf_jsthis/
http://www.cnblogs.com/snandy/archive/2012/09/01/2664134.html
http://stackoverflow.com/questions/9959727/proto-vs-prototype-in-javascript