JavaScript的 this的指向问题 bind, apply, call(更新中...arguments)caller, callee

js 的闭包与 this的指向可以说是js 进阶的必经之路;

推荐一篇写的非常好的文章: 比我写的好

手写call,apply,bind; 添加链接描述

this引用的是函数据以执行的环境对象

  • this的指向问题(严格与非严格)` this问题

==================================
随意编了三个函数(匿名函数调用时, 指向全局对象)

大家凑合着看吧

let name='hello world'
const obj={
	name:'obj',
	fn1:function sayHello(){
		let name='有名函数'
		console.log(this.name)
	},
	fn2:function (){
		let name='匿名函数'
		console.log(this.name)
	},
	fn3:()=>{
		let name='箭头函数'
		console.log(this.name)
	}
}
obj.fn1()  //obj
obj.fn2()	//obj
obj.fn3()  //hello world 或者undefined


  • this 在有名函数时,指向调用的对象
  • 匿名函数(非箭头函数): 也是指向调用的对象
  • 箭头函数时不确定的, 有调用对象的父级确定

1. 每个函数都包含非继承而来的方法:call()方法和apply()方法以及bind)()。
2. 相同点:这些方法的作用是一样的。

  • 值 得 在 说 道 的 是 其 实 使 用 n e w 的 时 候 就 是 改 变 t h i s 的 指 向 达 到 的 值得在说道的是其实 使用new的时候就是改变this的指向达到的 使newthis

可以用来改变this的指向;但是 使用apply() ,第二个是必须接收一个数组的参数形式

this的指向, 经常与匿名函数, 箭头的函数的使用有关, 当然有名函数也不例外,
一般可以先在函数声明前, let that =this , let _this=this, 先去改变this 的指向


详解: https://www.cnblogs.com/phoebeyue/p/9216514.html

/**1.匿名函数具有全局性,故匿名函数的this  指向window的对象;即是没有名字的函数
 * 2.箭头函数:与匿名函数还是有点区别,
 * 箭头函数看上去是匿名函数的一种简写,但实际上,箭头函数和匿名函数有个明显的区别:箭头函数内部的this是词法作用域,由上下文确定。

   由于JavaScript函数对this绑定的错误处理,下面的例子无法得到预期结果:
 * 3.函数表达式:属于匿名函数,并将匿名函数赋值给了一个变量;
 * 
 * 
 *  */


由于this在箭头函数中已经按照词法作用域绑定了,所以,用call()或者apply()调用箭头函数时,无法对this进行绑定

var obj = {
    birth: 1990,
    getAge: function (year) {
        var b = this.birth; // 1990
        var fn = (y) => y - this.birth; // this.birth仍是1990
        return fn.call({birth:2000}, year);
    }
};
obj.getAge(2015); // 25

两者的使用实例




// this 与call();  apply)();arguments的用法

function test(test1, test2) {
    console.log(test1 + test2);
}

function applyTest1(test1, test2) {
    // return test.apply(this,[test1,test2]); // 传入数组
    //return test.apply(this,arguments);  //arguments  为一个伪数组;

    //return test.call(this,test1,test2);//  apply与call用法的差别就在这里

    return test.call(this,arguments);//会出现问题;需要使用,
    ...arguments //先解构
}
applyTest1(4,6);



var name = "hello";
var person = {name: "小明"};
function sayHello () {
console.log(this.name )// hello
}




var personSay = sayHello.bind(person);  //bind的用法, 调用时,需要(), 进行使用
personSay(); 

在JS中,这三者都是用来改变函数的this对象的指向的,他们有什么样的区别呢。
在说区别之前还是先总结一下三者的相似之处:
1、都是用来改变函数的this对象的指向的。
2、第一个参数都是this要指向的对象。
3、都可以利用后续参数传参。

那么他们的区别在哪里的,先看一个例子。

 var xw = {
    name : "小王",
     gender : "男",
     age : 20,
     say : function() {
             alert(this.name + " , " + this.gender + " ,今年" + this.age);                                
        }
}
var xh = {
    name : "小红",
    gender : "女",
    age : 18
}
       xw.say();

那么如何用xw的say方法来显示xh的数据呢。
对于call可以这样:

xw.say.call(xh);

xw.say.apply(xh);

而对于bind来说需要这样:

xw.say.bind(xh)();

如果直接写xw.say.bind(xh)是不会有任何结果的,看到区别了吗?
call和apply都是对函数的直接调用

而bind方法返回的仍然是一个函数,因此后面还需要()来进行调用才可以。
那么call和apply有什么区别呢?我们把例子稍微改写一下。(体现在传参的时候)

注意写在函数内部的时候,可以使用arguments 来代替参数,达到传参的目的,
但是有值得注意的是, arguements, 默认是以数组的形式传入的, 对于apply, 没啥问题,但是对于call , 可能就需要先解构了



var xw = {
        name : "小王",
        gender : "男",
        age : 24,
        say : function(school,grade) {
                alert(this.name + " , " + this.gender + " ,今年" + this.age + " ,在" + school + "上" + grade);                                
        }
}
var xh = {
        name : "小红",
        gender : "女",
        age : 18
}

复制代码
可以看到say方法多了两个参数,我们通过call/apply的参数进行传参。
对于call来说是这样的

xw.say.call(xh,"实验小学","六年级");       

复制代码
而对于apply来说是这样的

xw.say.apply(xh,["实验小学","六年级大学"]);

复制代码
看到区别了吗,call后面的参数与say方法中是一一对应的,而apply的第二个参数是一个数组,数组中的元素是和say方法中一一对应的,这就是两者最大的区别。
那么bind怎么传参呢?它可以像call那样传参。

xw.say.bind(xh,"实验小学","六年级")();

但是由于bind返回的仍然是一个函数,所以我们还可以在调用的时候再进行传参。

xw.say.bind(xh)("实验小学","六年级");

当然我们也可以使用apply的特性,达到意向不到的amaze效果;
apply... 的互用

function f(x, y, z) {
  // ...
}
var args = [0, 1, 2];
f.apply(null, args);

// ES6的写法
function f(x, y, z) {
  // ...
}
let args = [0, 1, 2];
f(...args);

扩展运算符的具体使用:
https://blog.csdn.net/ZHXT__/article/details/100764870
ES6原理详解


caller 与 callee

  • caller 属性

    返回一个对函数的引用,即调用了当前函数的函数体。
    functionName.caller :functionName 对象是所执行函数的名称。

对 于函数来说,caller 属性只有在函数执行时才有定义。 如果函数是由 JS 程序的顶层调用的,那么 caller 包含的就是 null 。
如果在字符串上下文中使用 caller 属性,那么结果和 functionName.toString 一样,也就是说,显示的是函数的反编译文本。

var a = function() { 
alert(a.caller); 
} 
var b = function() { 
a(); 
} 
b();  //  ƒ () { a();}

///
var a = function() { 
alert(a.caller); 
} ;
a() //null

caller返回一个函数的引用,这个函数调用了当前的函数。
使用这个属性要注意:
1 这个属性只有当函数在执行时才有用
2 如果在javascript程序中,函数是由顶层调用的,则返回null


  • callee
    返回正被执行的 Function 对象,也就是所指定的 Function 对象的正文。
    [function.]arguments.callee:可选项 function 参数是当前正在执行的 Function 对象的名称。

说明
callee 属性的初始值就是正被执行的 Function 对象。
callee 属性是 arguments 对象的一个成员,它表示对函数对象本身的引用,
这有利于匿名 函数的递归或者保证函数的封装性,
例如下边示例的递归计算1到n的自然数之和。而该属性仅当相关函数正在执行时才可用。

还有需要注意的是callee拥有length属性,这个属性有时用于验证还是比较好的。arguments.length 是实参长度,arguments.callee.length 是形参长度,
由此可以判断调用时形参长度是否和实参长度一致

 function factorial(num){
        if(num<=1){
            return 1;
        }else{
            return num*factorial(num-1);
        }
    }
//递归更加具有说明性
  function factorial(num){
        if(num<=1){
            return 1;
        }else{
            return num*arguments.callee(num-1);
        }
    }

另一个具有迷惑性的例子

var a = function() { 
  console.log(arguments.callee); 
  } 
  var b = function() { 
  a(); 
  } 
  b(); //ƒ () { console.log(arguments.callee); }

总之,
callee, 是 arguments的一个属性, arguments.callee 指向的是函数的本身,
caller ,是ES5中的, 返回一个函数的引用

这篇文章对caller与 callee讲解的更加透彻:
https://www.jianshu.com/p/e1542e09869a

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值