2021-4-22 前端达人
this指向的问题也是JavaScript中的难点之一了,也是面试常问的问题,很多时候对this指向的问题就很懵逼,明明应该是指向他,为什么又指向他了…所以我就学习了一下这方面的知识,整理了一下,希望能够帮助大家
首先看一段代码
function identify(){
return this.name.toUpperCase()
}
function speak(){
var greeting = '你好,我是'+identify.call(this)
console.log(greeting)
}
var me ={
name:'kyle'
}
var you ={
name:"reader"
}
identify.call(me)
identify.call(you)
speak.call(me) //? 你好,我是KYLE
speak.call(you) //? 你好,我是READER
上面的这段代码中可以从不同的上下文对象 me 和 you 中重复的使用identify函数和speak函数
如果你不使用this的话 你就要显式的将上下文对象作为参数传递进去,比如这样:
function identify(context){
return context.name.toUpperCase()
}
function speak(context){
var greeting = '你好,我是'+identify(context)
console.log(greeting)
}
var me ={
name:'kyle'
}
var you ={
name:"reader"
}
identify(me)
identify(you)
speak(me)
speak(you)
就像这样,这样看起来就不想上面那样简洁了,你要把一个对象传来传去的
刚见到this的时候 觉得this指向是这个函数自身,或者是函数的作用域,后来发现其实不是这样的的,不过也不能说错了,因为有些情况确实是这样的,比如这样:
function foo(num){
console.log('foo'+ num)
this.count ++
}
foo.count = 0
var i;
for(i = 0;i<10;i++){
if(i>5){
foo.call(foo,i)
}
}
console.log(foo.count) //4 这样的话 this指向了foo本身 foo上面的count属性++
无法指向函数作用域
var a = 3
function foo() {
var a = 2;
bar.call(foo);
}
function bar() {
console.log( this.a );
}
foo(); // undefined
我们要记住非常重要的一点:this是在运行的时候进行绑定的,而不是在定义的时候绑定,this的绑定跟函数声明的位置没有关系,主要是取决于函数的调用方式,想要找到this指向谁,我们就要看懂函数是怎么调用的。
1.默认绑定
当一个独立函数正常调用的时候,不带任何修饰的调用
// 非严格模式下
var a = 3
function foo(){
console.log(this.a) //a
}
foo()
这种情况下 this.a被解析成了了 全局变量a,this指向是全局对象
// 严格模式下
var a = 3
function foo(){
"use strict"
console.log(this.a) //TypeError
}
foo()
严格模式下 this不会指向全局对象 this绑定的是undefined
2.隐式绑定
调用位置上是否有上下文对象
function foo(){
console.log(this.a)
}
var obj = {
a:2,
foo:foo
}
obj.foo() //2
调用位置会使用obj上下文对象来引用函数,foo被调用的时候 他的落脚点指向是obj对象,隐式绑定的规则就会把this指向这个上下文对象。所以this.a就跟 obj.a是一样的
function foo(){
console.log(this.a)
}
var obj = {
a:2,
foo:foo
}
var obj2 = {
a:3,
obj:obj
}
obj2.obj.foo() //2
当多层调用的时候 只有最后一层才会影响函数的调用位置 比如上面这个 this绑定的还是 obj 而不是obj2
注意
隐式绑定会出现隐式丢失的问题,会失去绑定对象,最后应用默认绑定
var a = 3;
function foo(){
console.log(this.a);
}
var obj = {
a:2,
foo:foo
}
var bar = obj.foo
bar() //3
bar 是 obj.foo的一个引用 他引用的是foo函数本身,此时bar就是一个不带任何修饰的函数调用 应用默认绑定
var a = 3;
function foo(){
console.log(this.a);
}
var obj = {
a:2,
foo:foo
}
setTimeout( obj.foo, 100 ) //3
setTimeout(function(fn){
fn()
},100,obj.foo) //3
参数传递也是一种隐式赋值,回调函数丢失this是非常常见的…
3.显式绑定
隐式绑定的时候我们必须在一个对象内部包含一个指向函数的属性,然后通过属性间接引用函数,把这个this间接隐式的绑定到这个对象上
如果我们不想在对象内部包含函数的引用 ,而想在某个对象上强制调用函数
我们可以把这个函数绑定到对象的原型上,也算是不用再对象内部包含函数了吧…
更好的办法是我们可以使用函数的 call() apply() bind() 这种方法
function foo(){
console.log(this.a)
}
var obj = {
a:2
}
foo.call(obj) //2
foo.apply(obj) //2
如果你第一个参数传入的是一个原始类型 比如字符串 布尔 数字作为绑定对象 这些原始类型会被转换为 对象的形式 new String() new Number()…
硬绑定
Function.prototype.bind()
function foo(){
console.log(this.a)
}
var obj = {
a:2
}
var obj2 = {
a:3
}
var bar = foo.bind(obj) //会返回一个硬编码的新函数 他会把参数设置为this的上下文
bar.call(obj2) //2 返回的新函数
有些api 方法 会提供一个可选参数 context 其作用跟bind一样 确保你的回调函数使用指定的this 比如 array.forEach(fn,context)…
4.new绑定
使用new 来调用函数的时候会执行以下操作
1.创建一个全新的对象
2.这个新对象会被执行原型的链接
3.新对象会绑定到函数调用的this
4.如果没有返回其他的对象,那么函数会自动返回这个对象
function Foo(a){
this.a = a
}
var bar = new Foo(2)
console.log(bar.a) //2
使用new 来调用Foo函数 会构造一个新对象并把它绑定到Foo调用中的this上 然后返回了
函数不带任何修饰的时候单独调用才会触发默认绑定 所以说默认绑定是优先级最低的了
那剩下三个规则哪个的优先级最高?
显示绑定跟隐式绑定比较
function foo(){
console.log(this.a)
}
var obj1 = {
a:1,
foo:foo
}
var obj2 = {
a:2,
foo:foo
}
obj1.foo() //1
obj2.foo() //2
obj1.foo.call(obj2) //2
obj2.foo.call(obj1) //1
可以看到 显示绑定的优先级还是更高一点
new 绑定跟隐式绑定比较
function foo(arg){
this.a = arg
}
var obj1 ={
foo:foo
}
var obj2 ={}
obj1.foo(2)
console.log(obj1.a) //2
var bar = new obj1.foo(4)
console.log(obj1.a) //2
console.log(bar.a) //4
可以看到 new绑定的优先级比隐式绑定要高
new 绑定跟显示绑定比较
new跟call apply无法一起使用 无法通过new foo.call(obj),试一下硬绑定
在这里插入代码片
function foo(arg){
this.a = arg
}
var obj1 ={}
var bar = foo.bind(obj1)
bar(3)
console.log(obj1.a) //3
var baz = new bar(4)
console.log(baz.a) //4
console.log(obj1.a) //3
new 调用bar修改了硬绑定时候的 函数的this new的优先级高一点
所以我们可以根据下面的优先级规则进行判断了
1.函数是否在new中调用 是的话this绑定新创建的对象 var bar = new Foo()
2.函数是否通过call apply 显示绑定或者是 bind硬绑定 如果是的话this指向指定的对象 foo.call(obj)
3.函数是否在某个上下文中调用 隐式绑定,如果是 this绑定那个上下文对象 注意绑定丢失的问题
4.如果都不是 就是默认绑定非严格模式下绑定的是全局对象 严格模式下绑定的是undefined
1.将null和undefined作为call apply参数 作为this绑定对象的时候 这些值会被忽略 应用的是默认绑定
var a =3
function foo(){
console.log(this.a) //3
}
foo.call(null)
2.箭头函数
function foo(){
return ()=>{
console.log(this.a)
}
}
var obj1 = {
a:3
}
var obj2 = {
a:4
}
var bar = foo.call(obj1)
bar.call(obj2) //3 this绑定的是obj1 而不是obj2!!!
在看一个
function foo(){
setTimeout(()=>{
console.log(this.a) //2
},100)
}
var obj = {
a:2
}
foo.call(obj)
箭头函数不使用this绑定的四种规则,而是根据外层作用域来决定this的,外层作用域的this绑定的是什么 他的this就是什么
转自:csdn 论坛 作者:Selfimpr欧
蓝蓝设计( www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 、平面设计服务
蓝蓝设计的小编 http://www.lanlanwork.com