this
就是js
裏的關鍵字,有特殊意義,表明函數執行主體。javascript
一、全局做用域裏的this
是window
,全局做用域下至關因而window.fn()
執行只是把window.
省略了(嚴格模式下是undefined
)。java
console.log(this === window) // true
window.a = 13;
console.log(this.a) // 13
複製代碼
二、函數裏的this
,看執行主體前有沒有點
,若是有點
,那點
前面是誰,函數裏的this
就是誰,若是沒有點
,那函數裏的this
就是window
,嚴格模式下是undefined
。數組
function fn(){
console.log(this)
}
fn();//window
let obj = {
fn:function(){
console.log(this)
}
}
obj.fn();//obj
var f = obj.fn;
f();//window
複製代碼
三、自執行函數裏的this
是window
或undefined
(嚴格模式下)app
(function(){
console.log(this);//==>window
})();
~function(){}();//==>window
+function(){}();//==>window
-function(){}();//==>window
!function(){}();//==>window
複製代碼
四、回調函數裏的this
通常狀況下是window
函數
let ary = [1,2,3];
ary.forEach(function(item,index){
console.log(this)
})
//================================//
setTimeout(function(num){
console.log(num)
console.log(this)
},1000,1)
//================================//
function fn(m){
m()
}
fn(function(){
console.log(this)
})
複製代碼
五、箭頭函數沒有this
:ui
可是要是在箭頭函數裏使用
this
,他就會往上一級做用域查找,若是上一級做用域也沒有,那就繼續往上找,直到找到全局的window
爲止this
let obj = {
num: 2,
fn: function () {
// this-->obj
let m = () => {
// this-->obj
console.log(this.num) // obj(向上一級做用域查找)
}
m()
}
}
obj.fn()
複製代碼
六、構造函數裏的this
是當前實例spa
七、實例原型上的公有方法裏的this
通常是當前實例(下面改變this
的方法中有體現)prototype
八、給元素綁定事件行爲,那事件裏的this
就是當前被綁定的元素自己code
btn.onclick = function(){
console.log(this) // btn
}
複製代碼
總結下來
this
在面向對象中,主要仍是看是誰執行的,也就是執行函數點前面是誰
- 一、方法執行,看前面是否有點,點前面是誰
THIS
就是誰- 二、把方法總的
THIS
進行替換- 三、再基於原型鏈查找的方法肯定結果便可
this
指向:call
/apply
/bind
每個函數(普通函數/構造函數/內置類)都是
Function
這個內置類的實例,因此:函數.__proto__===Function.prototype
,函數
能夠直接調取Function
原型上的方法
call / apply / bind
- 原型上提供的三個公有屬性方法
- 每個函數均可以調用這個方法執行
- 這些方法都是用來改變函數中的
THIS
指向的
Function.prototype.cal
l這個方法,而且把它執行,在call
方法執行的時候改變裏面的this關鍵字THIS
指向改成第一個傳遞給CALL
的實參CALL
其他的實參,當作參數信息傳遞給當前函數CALL
一個實參都沒有傳遞,非嚴格模式下是讓函數中的THIS
指向WINDOW
,嚴格模式下指向的是UNDEFINED
fn.call(null)
;
this:window
null
(第一個參數傳遞的是null
/undefined
/不傳
,非嚴格模式下this
指向window
,嚴格模式下傳遞的是誰this
就是誰,不傳this
是undefined
)function fn(){}
fn.call(); //=>fn函數基於原型鏈找到Function.prototype上的call方法,而且讓其執行(執行的是call方法:方法中的this是fn)
fn.call.call(); //=>fn.call就是Function.prototype上的call方法,也是一個函數,只要是函數就能用原型上的方法,因此能夠繼續調用call來執行
Function.prototype.call = function $1(){
//...
}
fn.call => $1
fn.call() => $1() this:fn
fn.call.call() => $1.call() => 繼續讓call執行,this:$1
實例.方法():都是找到原型上的內置方法,讓內置方法先執行(只不過執行的時候作了一些事情會對實例產生改變,而這也是這些內置方法的做用),內置方法中的THIS通常都是當前操做的實例
複製代碼
```javascript
//=>咱們的需求是想讓FN執行的時候,方法中的THIS指向OBJ
obj.fn(); //=>Uncaught TypeError: obj.fn is not a function
//由於此時obj中並無fn這個屬性
-------解決辦法---------
obj.fn = fn;
obj.fn(); //=>this:obj //=>'OBJ'
delete obj.fn;//=>對象中本來沒有,因此使用後要刪掉
```
複製代碼
詳細梳理:
~ function () {
/* * myCall:改變函數中的THIS指向 * @params * context 能夠不傳遞,傳遞必須是引用類型值(由於後面要給它加$fn的屬性) */
function myCall(context) {
//this:sum 也就是當前要操做的這個函數實例
context = context || window;
let args = [], //=>除第一個參數外剩餘傳遞的信息值
result;
for (let i = 1; i < arguments.length; i++) {
args.push(arguments[i]);
}
context.$fn = this;
result = context.$fn(...args); //=>args=[10,20] ...是ES6中的展開運算符,把數組中的每一項分別的展開傳遞給函數 //=>context.$fn(10,20)
delete context.$fn;
return result;
}
/* 擴展到內置類的原型上 */
Function.prototype.myCall = myCall;
}();
複製代碼
可簡寫爲
function myCall(context,...arg){
//context-->obj this-->fn
context = context || window; // 若是你不傳參、或者傳null、傳undefined,那context的值都是window
let res = null; // 建立一個變量,準備接收函數執行結果
context.fn = this; // 把fn增長到obj裏
res = context.fn(...arg); // 讓fn指向
delete context.fn; // 把fn從obj裏刪除
return res; // 把this的返回值return出去
}
Function.prototype.myCall = myCall;
console.log(fn.myCall(obj,1,2)) // 'ss'
複製代碼
以題爲例分析
最終咱們能夠得出:
- 一個
CALL
是讓左邊函數執行(this
是傳遞的參數)- 多個
CALL
是讓最後傳參的函數執行(this
是window
/undefined
)
和
call
方法同樣,都是把函數執行,而且改變裏面的this
關鍵字的,惟一的區別就是傳遞給函數參數的方式不一樣
call
是一個個傳參apply
是按照數組傳參
let obj={name:'OBJ'};
let fn=function(n,m){
console.log(this.name);
}
//=>讓fn方法執行,讓方法中的this變爲obj,而且傳遞10/20
fn.call(obj,10,20);
fn.apply(obj,[10,20]);
複製代碼
和
call
/apply
同樣,也是用來改變函數中的this
關鍵字的,只不過基於bind
改變this
,當前方法並無被執行,相似於預先改變this
bind
是預處理this
,他並不會讓函數執行bind
方法的返回值是一個改變this
以後的新函數THIS
指向經過預處理的方式改成第一個傳遞給BIND
的實參let obj={name:'OBJ'};
function fn(){
console.log(this.name);
}
document.body.onclick=fn; //=>當事件觸發,fn中的this:BODY
//=>點擊BODY,讓FN中的THIS指向OBJ
//document.body.onclick=fn.call(obj); //=>基於call/apply這樣處理,不是把fn綁定給事件,而是把fn執行後的結果綁定給事件
document.body.onclick=function(){
//this:BODY
fn.call(obj);
}
document.body.onclick=fn.bind(obj);
複製代碼
IE6~8
中不支持bind
方法bind
的好處是:經過bind
方法只是預先把fn
中的this
修改成obj
,此時fn
並無執行呢,當點擊事件觸發纔會執行fn
(call
/apply
都是改變this
的同時當即把方法執行)