來直接上代碼javascript
function foo(){
console.log(`我自己屬性a是 ${this.a}`)
}
var bar ={
a:2,
foo:foo
}
var baz={
a:4,
foo:foo
}
bar.foo();//我自己屬性a是 2
baz.foo()://我自己屬性a是 4
複製代碼
小夥伴們是否是已經在這個簡單的代碼中發現了 剛發foo只定義了一次,去能夠被不一樣的對象引用,實現了代碼共享java
接下來我們看看代碼中的是調試es6
接下來說用到函數的是兩個身份普通函數、普通對象, 看代碼()數組
function foo(){
this.count++
}
var count=0;
foo.count=0;
for(var i=0;i<5;i++){
foo()
}
console.log(foo.count)//0
console.log(count)//5
複製代碼
從打印的結果上來看顯然,this指向的不是自己函數,固然我們通常看到這類的問題我們就會繞道而行,看代碼安全
function foo(){
this.count++
}
var bar={
count:0
}
foo.count=0;
for(var i=0;i<5;i++){
foo.call(bar)
}
console.log(bar.count)//5
console.log(count)//0
複製代碼
雖然這種解決方案很好,也會有其餘的解決方案,可是咱們仍是不理解this的問題,內心仍是有種不安之感
bash
function foo(){
var num=2;
console.log(this.num)
}
var num=0;
foo()//0
複製代碼
我們看到代碼的執行結果後,發現this指向的並非該函數的做用域。閉包
this是在函數調用的時候綁定,不是在函數定義的時候綁定。它的上下文取決於函數調用時的各類條件,函數執行的時候會建立一個活動記錄,這個記錄裏面包含了該函數中定義的參數和參數,包含函數在哪裏被調用(調用棧)...,this就是其中的一個屬性。 來看圖 app
圖中我們看到this是在函數執行的時候建立的。函數
前面幾步我們已經肯定的this的建立和this的指向的誤區,接下啦我們要看看this的綁定的規則,分爲4個規則。學習
function foo(){
var num=2;
this.num++
console.log(this.num)
}
var num=0;
foo()//1
複製代碼
上面代碼中就實現了默認綁定,在foo方法的代碼塊中操做的是window.num++。
function foo(){
console.log(this.name)
}
var bar={
name:'shiny',
foo:foo
}
bar.foo()//shiny
複製代碼
要須要補充一點,無論你的對象嵌套多深,this只會綁定爲直接引用該函數的地址屬性的對象,看代碼
function foo(){
console.log(this.name)
}
var shiny={
name:'shiny',
foo:foo
}
var red={
name:'red',
obj:shiny
}
red.obj.foo()//shiny
複製代碼
function foo(){
console.log(this.name)
}
var shiny={
name:'shiny',
foo:foo
}
function doFoo(fn){
fn()
}
doFoo(shiny.foo)//undefind
複製代碼
你們知道函數參數在函數執行的時候,其實有一個賦值的操做,我來解釋一下上面的,當函數doFoo執行的時候會開闢一個新的棧並被推入到全局棧中執行,在執行的過程當中會建立一個活動對象,這個活動對象會被賦值傳入的參數以及在函數中定義的變量函數,在函數執行時用到的變量和函數直接從該活動對象上面取值使用。 看圖 doFoo的執行棧
fn的執行棧
看下面原理和上面同樣經過賦值,致使隱式綁定的丟失,看代碼
function foo(){
console.log(this.name)
}
var shiny={
name:'shiny',
foo:foo
}
var bar = shiny.foo
bar()//undefined
複製代碼
你們是否是已經明白了爲何是undefined,來解釋一波,其實shiny的foo屬性是引用了foo函數的引用內存地址,那麼有把foo的引用地址賦值給了 bar 那麼如今的bar的引用地址個shiny.foo的引用地址是一個,那麼執行bar的時候也會觸發默認綁定規則由於沒有其餘規則能夠匹配,bar函數執行時,函數內部的this綁定的是全局變量。
看下滿的引用地址賦值是出現的,奇葩 隱式綁定丟失,看代碼
function foo(){
console.log(this.name)
}
var shiny={
name:'shiny',
foo:foo
}
var red={
name:'red'
}
(red.foo=shiny.foo)()//undefined
複製代碼
賦值表達式 p.foo = o.foo 的返回值是目標函數的引用,所以調用位置是 foo() 而不是 p.foo() 或者 o.foo()。根據咱們以前說過的,這裏會應用默認綁定。
看代碼
function foo(){
console.log(this.age)
}
var shiny={
age:20
}
foo.call(shiny)//20
function bar(){
console.log(this.age)
}
var red={
age:18
}
bar.apply(red)//18
複製代碼
這兩個方法都是顯式的綁定了tihs
function foo(b){
return this.a+b
}
var obj={
a:2
}
function bind(fn,obj){
return function(){
return fn.apply(obj,arguments)
}
}
bind(foo,obj)(3)//5
複製代碼
語言解釋: 經過apply + 閉包機制 實現bind方法,實現強行綁定規則
API調用的「上下文」 第三方庫或者寄生在環境,以及js內置的一些方法都提供了一下 content 上下文參數,他的做用和 bind同樣,就是確保回調函數的this被綁定
function foo (el){
console.log(el,this.id)
}
var obj ={
id:'some one'
};
[1,2,4].forEach(foo,obj)
// 1 some one 2 some one 4 some one
複製代碼
傳統面向類的語言中的構函數,是在使用new操做符實例化類的時候,會調用類中的一些特殊方法(構造函數)
不少人認爲js中的new操做符和傳統面向類語言的構造函數是同樣的,其實有很大的差異
重新認識一下js中的構造函數,js中的構造函數 在被new操做符調用時,這個構造函數不屬於每一個類,也不會創造一個類,它就是一個函數,只是被new操做符調用。
使用new操做符調用 構造函數時會執行4步
我們瞭解了js new 操做符調用構造函數時都作了些什麼,哪麼我們就知道構造函數裏面的this是誰了
代碼實現
function Foo(a){
this.a=a
}
var F = new Foo(2)
console.log(F.a)//2
複製代碼
看代碼
function foo(){
console.log(this.name)
}
var shiny={
name:'shiny',
foo:foo
}
var red={
name:'red'
}
shiny.foo()//shiny
shiny.foo.call(red)// red
shiny.foo.apply(red)// red
shiny.foo.bind(red)()//red
複製代碼
顯然在這場綁定this比賽中,顯式綁定贏了隱式綁定
function foo(name){
this.name=name
}
var shiny={
foo:foo
}
shiny.foo('shiny')
console.log(shiny.name)//shiny
var red = new shiny.foo('red')
console.log(red.name)//red
複製代碼
顯然在這場綁定this比賽中new 操做符綁定贏了隱式綁定
使用call、apply方法不能結合new操做符會報錯誤
function foo(){
console.log(this.name)
}
var shiny={
name:'shiny'
}
var bar = foo.bind(shiny)
var obj = new bar();
console.log(obj.name)// undefind
複製代碼
顯然 new操做符綁定 打敗了 顯式綁定
function foo(){
console.log(name)
}
var name ='shiny'
foo.call(null)//shiny
foo.call(undefined)//shiny
var bar = foo.bind(null)
var baz = foo.bind(undefined)
bar()//siny
baz()//siny
複製代碼
把 null、undefined經過 apply、call、bind 顯式綁定,雖然實現可默認綁定,可是建議這麼作由於在非嚴格的模式下會給全局對象添加屬性,有時候會形成不可必要的bug。
function foo(a,b) {
console.log( "a:" + a + ", b:" + b );
}
// 咱們的空對象
var ø = Object.create( null );
// 把數組展開成參數
foo.apply( ø, [2, 3] ); // a:2, b:3
// 使用 bind(..) 進行柯里化
var bar = foo.bind( ø, 2 );
bar( 3 ); // a:2, b:3
複製代碼
function foo(){
return ()=>{
console.log(this.name)
}
}
var obj ={
name:'obj'
}
var shiny ={
name:'shiny'
}
var bar = foo.call(obj);
bar.call(shiny)// foo
複製代碼
咱們看到箭頭函數的this被綁定到該函數執行的做用域上。
我們在看看 js內部提供內置函數使用箭頭函數
function foo() {
setTimeout(() => {
// 這裏的 this 在此法上繼承自 foo()
console.log( this.a );
},100);
}
var obj = {
a:2
};
foo.call( obj ); // 2
複製代碼
function foo() {
var self = this; // lexical capture of this
setTimeout( function(){
console.log( self.a );
}, 100 );
}
var obj = {
a: 2
};
foo.call( obj ); // 2
複製代碼
雖然 self = this 和箭頭函數看起來均可以取代 bind(..),可是從本質上來講,它們想替 代的是 this 機制。 若是你常常編寫 this 風格的代碼,可是絕大部分時候都會使用 self = this 或者箭頭函數。 若是徹底採用 this 風格,在必要時使用 bind(..),儘可能避免使用 self = this 和箭頭函數。
若有不足,在評論中提出,我們一塊兒學習