首页 > 其他分享 >js的this指向

js的this指向

时间:2022-10-13 17:33:34浏览次数:72  
标签:console log 指向 绑定 js obj var fnn


作者::Wflynn

什么是函数的调用位置

调用位置就是函数在代码中被调用的位置(而不是声明的位置)

为什么要了解调用位置:只有了解函数的调用位置才能进一步的确定 ​​this​​ 的绑定对象

function baz () {
// 当前调用栈是:baz, 因此,当前调用位置是全局作用域
console.log("baz");
bar(); // bar 的调用位置
}

function bar () {
// 当前调用栈是 baz -> bar,因此,当前调用位置在 baz 中
console.log("bar");
fnn(); // fnn 的调用位置
}

function fnn () {
// 当前调用栈是 baz -> bar -> fnn, 因此,当前调用位置在 bar 中
console.log("fnn");
}
baz(); // baz 的调用位置

this 是什么

​this​​ 是包含它的函数作为方法被调用时所属的对象。

  • 包含它的函数。
  • 作为方法被调用时。
  • 所属的对象。

随着函数使用场合的不同,this 的值会发生变化。this 指向什么,完全取决于什么地方以什么方式调用,而不是创建时。

this 的四种绑定规则

​this​​​ 的 ​​4​​​ 种绑定规则分别是:默认绑定、隐式绑定、显式绑定、​​new​​ 绑定。优先级从低到高。

​new​​ 绑定 > 显式绑定> 隐式绑定 > 默认绑定

默认绑定

最常用的函数调用类型:独立函数调用。可以把这条规则看作是无法应用其他规则时的默认规则。

如代码(非严格模式下)和图片所示

  • ​this.a​​​ 被解析成了​​window.a​​​,​​fnn​​​ 中的​​this​​​ 是等于​​window​​ 的。
  • 由于​​fnn()​​​ 是直接使用不带任何修饰的函数引用进行调用的,因此只能使用默认绑定,无法应用其他规则,因此​​this​​ 指向全局对象。。
function fnn() { 
console.log(this);
console.log(this === window);
console.log(this.a);
}
var a = 2;
fnn(); // 2


js的this指向_函数调用

隐式绑定

调用位置是否有上下文对象,或者说是否被某个对象拥有或者包含

如代码(非严格模式下)和图片所示

  • ​this.a​​​ 被解析成了​​obj.a​​​,​​fnn​​​ 中的​​this​​​ 是等于​​obj​​ 的。
  • 由于调用​​fnn()​​​ 函数时,有引用上下文对象​​obj​​​,隐式绑定规则会把函数调用中的​​this​​​ 绑定到这个上下文对象​​obj​​​,因此​​this​​​ 指向​​obj​​ 对象。。
function fnn() { 
console.log(this);
console.log(this === obj);
console.log(this.a);
}
var obj = {
a: 2,
fnn: fnn
};

obj.fnn(); // 2


js的this指向_前端_02

对象属性引用链中只有最顶层或者说最后一层会影响调用位置

如代码所示,最后输出的 ​​this.a​​​ 等于 ​​888​​​,因为最后调用 fnn 的上下文对象是 ​​obj1​​​,所以 this 绑定在 ​​obj1​​ 上

function fnn() { 
console.log(this);
console.log(this === obj1); // true
console.log(this.a); // 888
}

var obj1 = {
a: 888,
fnn: fnn
};

var obj = {
a: 2,
obj1: obj1
};

obj.obj1.fnn(); // this.a 输出 888

隐式丢失

一个最常见的 ​​this​​​ 绑定问题就是被隐式绑定的函数会丢失绑定对象,也就是说它会应用默认绑定,从而把 ​​this​​​ 绑定到全局对象或者 ​​undefined​​ 上,取决于是否是严格模式。

如代码所示,最后输出的 ​​this.a​​​ 等于 ​​888​​​,虽然 ​​bar​​​ 是 ​​obj.fnn​​​ 的一个引用,但是实际上,它引用的是 ​​fnn​​​ 函数本身,
因此此时的 ​​​bar()​​ 其实是一个不带任何修饰的函数调用,因此应用了默认绑定。

function fnn() { 
console.log(this); // window 对象
console.log(this == window); // true
console.log(this.a); // 888
}
var obj = {
a: 2,
fnn: fnn
};

var bar = obj.fnn;

var a = "888"; // a 是全局对象的属性
bar(); // 888

回调函数中的例子

function fnn() { 
console.log(this); // window 对象
console.log(this == window); // true
console.log(this.a); // 888
}

function doFnn(fn) {
// fn 其实引用的是 fnn
fn(); // <-- 调用位置!
}

var obj = {
a: 2,
fnn: fnn
};

var a = "888"; // a 是全局对象的属性
doFnn(obj.fnn); // 888

显式绑定

使用 ​​call​​​,​​apply​​​ 或者 ​​bind​​ 方法绑定

call

​call()​​​ 方法使用一个指定的 ​​this​​ 值和单独给出的一个或多个参数来调用一个函数

function fnn(arg1,) { 
console.log(this);
console.log(this === obj); // true
console.log(this.a); // 888
console.log(arg1, arg2); // 参数一 参数二
}
var obj = {
a: 888,
fnn: fnn
};
var a = 2
fnn.call(obj, '参数一', '参数二'); // 888

apply

​apply()​​​ 方法调用一个具有给定 ​​this​​ 值的函数,以及以一个数组(或类数组对象)的形式提供的参数。

function fnn(arg1,) { 
console.log(this);
console.log(this === obj); // true
console.log(this.a); // 888
console.log(arg1, arg2); // 参数一 参数二
}
var obj = {
a: 888,
fnn: fnn
};
var a = 2
fnn.apply(obj, ['参数一', '参数二']); // 888

bind

​bind()​​​ 方法创建一个新的函数,在 ​​bind()​​​ 被调用时,这个新函数的 ​​this​​​ 被指定为 ​​bind()​​ 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。

function fnn(arg1,) { 
console.log(this);
console.log(this === obj); // true
console.log(this.a); // 888
console.log(arg1, arg2); // 参数一 参数二
}
var obj = {
a: 888,
fnn: fnn
};
var a = 2
const fnnBind = fnn.bind(obj, '参数一', '参数二');
fnnBind() // 888

​bind​​​ 与 ​​apply​​​ 和 ​​call​​​ 的区别是,​​bind​​ 是创建一个函数,但不会直接调用

new 绑定

使用 ​​new​​​ 来调用 ​​People(..)​​​ 时,我们会构造一个新对象并把它绑定到 ​​People(..)​​​ 调用中的 ​​this​​ 上。

代码示例

// 声明一个构造函数
function People(name){
console.log(fnn != this); // true
this.name = name;
}

// 使用 new 创建实例对象 fnn
var fnn = new People("FX");
console.log(fnn);
console.log(fnn.name) // FX;

如代码和图片所示,我们 ​​new​​ 一个实例对象,代码执行过程如下

var fnn = {} // 创建一个空对象; 或者 var fnn = new Object() 
fnn.__proto__ = People.prototype // 将该对象 fnn 的隐式原型指向构造函数显式原型
People.call(fnn, "FX") // 将构造函数中 this 指向创建的对象 fnn,并传入参数 "FX"
return fnn // 返回对象 fnn,person 指向创建的对象 fnn(对象类型赋值为按引用传递,fnn 与 person 指向同一个对象)

为什么 ​​console.log(fnn != this)​​​ 的值为 ​​true​​​,关于这一点我还不是特别理解。
按照我得想法,可能是在 ​​​new​​ 对象实例的过程中,实例对象实际并没有创建完毕,导致的不相等,如果有更好的理解,欢迎大家留言。

new 绑定遇到 ​retrun​

function fnn () {
this.user = 'fx'
return {}
}
var a = new fnn()
console.log(a.user) // undefined
function fnn () {
this.user = 'fx'
return function () {
}
}
var a = new fnn()
console.log(a.user) // undefined
function fnn () {
this.user = 'fx'
return 1
}
var a = new fnn()
console.log(a.user) // fx
function fnn () {
this.user = 'fx'
return undefined
}
var a = new fnn()
console.log(a.user) // fx
function fn () {
this.user = 'fx'
return null
}
var a = new fn
console.log(a.user) // fx

如果返回值是一个对象,那么 ​​this​​​ 指向的就是那个返回的对象,如果返回值不是一个对象那么 ​​this​​​ 还是指向函数的实例。
还有一点就是虽然 ​​​null​​​ 也是对象,但是在这里 ​​this​​ 还是指向那个函数的实例。

优先级测试

显示绑定 与 隐式绑定

如下代码,可以看出 显示绑定 优先级大于 隐式绑定

function fnn () {
console.log(this.a)
}

var obj1 = {
a: 2,
fnn: fnn
}
var obj2 = {
a: 3,
fnn: fnn
}
obj1.fnn() // 2
obj2.fnn() // 3
obj1.fnn.call(obj2) // 3
obj2.fnn.call(obj1) // 2

new 绑定 与 隐式绑定

如下代码,可以看出 ​​new​​ 绑定 优先级大于 隐式绑定

function fnn (num) {
this.a = num
}

var obj1 = {
a: 2,
fnn: fnn
}
var bar = new obj1.fnn(4);
console.log(obj1.a); // 2
console.log(bar.a); // 4

new 绑定 与 显示绑定

如下代码,可以看出 ​​new​​ 绑定 优先级大于 显示绑定

function fnn (num) {
this.a = num
}

var obj1 = {
a: 2,
}
var bar = fnn.bind(obj1);
bar(888)
console.log(obj1.a); // 888

var baz = new bar(666);
console.log(obj1.a); // 888
console.log(baz.a); // 666

this 丢失的情况

如果你把 ​​null​​​ 或者 ​​undefined​​​ 作为 ​​this​​​ 的绑定对象传入 ​​call​​​、​​apply​​​ 或者 ​​bind​​,这些值在调用时会被忽略,实际应用的是默认绑定规则,如下代码

function fnn() { 
console.log(this.a);
}

var a = 2;
fnn.call(null); // 2

间接引用的问题

如下代码,赋值表达式 ​​p.fnn = o.fnn​​​ 的返回值是目标函数的引用,因此调用位置是 ​​fnn()​​​ 而不是 ​​p.fnn()​​​ 或者 ​​o.fnn()​​,这里会应用默认绑定

function fnn () {
console.log(this.a)
}

var a = 2
var o = {
a: 3,
fnn: fnn
}
var p = {
a: 4
}
o.fnn(); // 3
(p.fnn = o.fnn)() // 2

箭头函数的 this

如下代码所示,​​fnn()​​​ 内部创建的箭头函数会捕获调用时 ​​fnn()​​​ 的 ​​this​​​。由于 ​​fnn()​​​ 的 ​​this​​​ 绑定到 ​​obj1​​​,
​​​bar​​​(引用箭头函数)的 ​​this​​​ 也会绑定到 ​​obj1​​​,箭头函数的绑定无法被修改。(​​new​​ 也不行!)

function fnn () {
// 返回一个箭头函数
return () => {
// this 继承自 fnn()
console.log(this.a)
}
}

var obj1 = {
a: 2
}
var obj2 = {
a: 3
}
var bar = fnn.call(obj1)
bar.call(obj2) // 2

非箭头函数,输出 ​​3​

function fnn () {
// 返回一个箭头函数
return function () {
// this 继承自 fnn()
console.log(this.a)
}
}

var obj1 = {
a: 2
}
var obj2 = {
a: 3
}
var bar = fnn.call(obj1)
bar.call(obj2) // 3

测试题

示例一

var o = {
a:10,
b:{
a:12,
fn:function(){
console.log(this.a); // 12
}
}
}
o.b.fn(); // 12

var o = {
a:10,
b:{
// a:12,
fn:function(){
console.log(this.a); //undefined
}
}
}
o.b.fn(); // undefined

示例二

var o = {
a: 10,
b: {
a: 12,
fn: function () {
console.log(this.a) //undefined
console.log(this) //window
}
}
}
var j = o.b.fn
j()

示例三

var x = 10
var obj = {
x: 20,
f: function () {
console.log(this.x) // 20
function fnn () {
console.log(this.x)
}
fnn() // 10 默认绑定,这里 this 绑定的是 window
}
}
obj.f()

示例四

  • ​fnn(1)​​​ 使用默认绑定,​​this.a = arg​​​ 相当于​​window.a = arg​​​,​​return this​​​ 相当于​​return window​
  • ​var a = fnn(1)​​​ 相当于​​window.a = window​​​,所以​​a.a​​​ 等于​​window​​​,依次类推​​a.a.a.a​​ 仍旧是 window
  • ​fnn(10)​​​ 使用默认绑定,​​this.a = 10​​​ 相当于​​window.a = 10​​​,​​return this​​​ 相当于​​return window​
  • ​console.log(b.a)​​​ 相当于​​window.a​​ 等于 10
function fnn (arg) {
this.a = arg
return this
}
var a = fnn(1)
console.log(a.a) // window

var b = fnn(10)

console.log(b.a) // 10

示例五

var x = 10
var obj = {
x: 20,
f: function () {
console.log(this.x)
}
}
var bar = obj.f
var obj2 = {
x: 30,
f: obj.f
}
obj.f() // 20
bar() // 10
obj2.f() // 30


标签:console,log,指向,绑定,js,obj,var,fnn
From: https://blog.51cto.com/u_13028258/5754437

相关文章

  • js打印输出九九乘法表
    for(leti=1;i<=9;i++){                    letstr='';                    for(letj=1;j<=i;j++)......
  • js中==、===、!=、!==的使用及区别
    代码样例varnum=1;varstr='1';vartest=1;test==num//true相同类型相同值test===num//true相同类型相同值test!==num//falsetest与num类......
  • js操作DOM为什么会影响性能
    DOM是什么?DOM(DocumentObjectModel——文档对象模型)是用来呈现以及与任意HTML或XML文档交互的API。DOM是载入到浏览器中的文档模型,以节点树的形式来表现文档,每个节点代......
  • JS判断数据类型
    怎么判断对象类型?可以通过​​Object.prototype.toString.call(xx)​​​。这样我们就可以获得类似​​[objectType]​​的字符串。​​instanceof​​​可以正确的判断......
  • JS实现二分搜索
    二分查找的前提为:数组、有序。逻辑为:优先和数组的中间元素比较,如果等于中间元素,则直接返回。如果不等于则取半继续查找。非递归实现functionbinarySearch(arr,target){......
  • JS面试点- bind / call / apply
    bind/call/apply可用于this的显式绑定this绑定的是call,apply,bind的第一个参数​​call()方法​​vara={user:'fx',fn:function(){console.......
  • 在考虑闭包的情况下JS变量存储在栈与堆的区分
    变量存储在闭包中的问题按照常理来说栈中数据在函数执行结束后就会被销毁,那么 ​​JavaScript​​ 中函数闭包该如何实现,先简单来个闭包:functioncount(){letnum=......
  • js 输入框中过滤表情,颜文字(正则)
    letname=this.name //this.name为输入框中的输入内容 varregStr=/[\uD83C|\uD83D|\uD83E][\uDC00-\uDFFF][\u200D|\uFE0F]|[\uD83C|\uD83D|\uD83E][\uDC00-\uD......
  • JS判断数组中是否包含某个值
    方法一:array.indexOf此方法判断数组中是否存在某个值,如果存在,则返回数组元素的下标,否则返回-1。vararr=[1,2,3,4]varindex=arr.indexOf(3)console.log(index)方法......
  • JS实现继承的方法
    方法一:借助callfunctionParent(sex){this.name='fx'this.sex=sex}Parent.prototype.test=function(){console.log('我是函数')}Parent.prototype.wh......