来直接上代码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 和箭头函数。
若有不足,在评论中提出,我们一块儿学习