call()的用法以及借用方法思想的解读

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

简言之:设置函数体内this对象的值

var a = 11;
var b = 12;
function add(c, d){
  console.log(this.a + this.b + c + d); 
} 
var o = {a:1, b:3}; 
add(5, 7); // 35
add.call(this, 5, 7); // 35
add.call(window, 5, 7); // 35
add.call(o, 5, 7); // 16
function test(a,b,c,d) { 
  var arg = Array.prototype.slice.call(arguments,1); 
  console.log(arg);
} 
test("a","b","c","d"); // b,c,d

注意:

(1)、MDN上关于slice()处理类数组的解释: slice 方法可以用来将一个类数组(Array-like)对象/集合转换成一个新数组。你只需将该方法绑定到这个对象上。 一个函数中的arguments就是一个类数组对象的例子;

(2)、slice()若不传参数则默认从0开始提取数组元素,返回新提取后的数组;

解释:

(1)、因为arguments并不是真正的数组对象,只是类数组,所以它并没有slice这个方法,而Array.prototype.slice.call(arguments, 1)可以理解成是让arguments转换成一个数组对象,让arguments具有slice()方法,要是直接写arguments.slice(1)会报错。其实这里相当于在test()函数内执行了arguments.slice(1),这种方法的使用又称为借用方法

(2)、除了使用 Array.prototype.slice.call(arguments),你也可以简单的使用 [].slice.call(arguments) 来代替;

(3)、还可以使用bind简化该过程(下面这段代码可忽略,和接下来要讲的代码是同一段):

var slice = Function.prototype.call.bind(Array.prototype.slice);
function list() {
  return slice(arguments);
}
console.log(list(1, 2, 3)); // [1, 2, 3]
var slice = Function.prototype.call.bind(Array.prototype.slice)
function list() {
  return slice(arguments);
}
console.log(list(1, 2, 3)); // [1, 2, 3]

var join = Function.prototype.call.bind(Array.prototype.join);
console.log(join('abc', '|')); // 'a|b|c'

解释:

(1)、Function.prototype.call是一个引用,用来调用一个函数(slice())并且把它的“this”值设置为其内部提到的方法;

(2)、记住“bind”返回一个新的函数,这个函数总是会牢记它的“this”值,因此,.bind(Array.prototype.slice)会返回一个新的函数,它的“this”被永久地设置成了 Array.prototype.slice 函数;

(3)、通过结合以上两个,我们现在有了一个新的函数,它将会调用“call”函数并且“this”限定为了“slice”函数,简单地调用slice()便可以引用之前限定的方法。join()原理相同;

var toUpperCase = Function.prototype.call.bind(String.prototype.toUpperCase);
console.log(String.prototype.toUpperCase.call(['lowercase', 'words', 'in', 'a', 'sentence']));
// LOWERCASE,WORDS,IN,A,SENTENCE
console.log(toUpperCase(['lowercase', 'words', 'in', 'a', 'sentence']).split(','));
// ["LOWERCASE", "WORDS", "IN", "A", "SENTENCE"]

解释:

String.prototype.toUpperCase.call()会将任何非字符串形式的值转换成字符串, 当你把这个值的 this 设置成一个不是字符串的值;  


注意: 

接下来要讲的也是和call()有关,就是大家公认的判断所有js类型的通用方法Object.prototype.toString.call()

var obj = {
  a: 2
}
console.log(Object.prototype.toString.call(obj) === "[object Object]"); // true

相信 Object.prototype.toString.call()方法对于大家来说并不陌生,下面来解释下为什么要这样判断数据类型:

console.log(Object.prototype.toString.call("jerry")); // [object String]
console.log(Object.prototype.toString.call(12)); // [object Number]
console.log(Object.prototype.toString.call(true)); // [object Boolean]
console.log(Object.prototype.toString.call(undefined)); // [object Undefined]
console.log(Object.prototype.toString.call(null)); // [object Null]
console.log(Object.prototype.toString.call({name: "jerry"})); // [object Object]
console.log(Object.prototype.toString.call(function(){})); // [object Function]
console.log(Object.prototype.toString.call([])); // [object Array]
console.log(Object.prototype.toString.call(new Date)); // [object Date]
console.log(Object.prototype.toString.call(/\d/)); // [object RegExp]
function Person(){};
console.log(Object.prototype.toString.call(new Person)); // [object Object]

Object.prototype.toString()方法的解释说:toString() 方法返回一个表示该对象的字符串;

为什么我们不直接调用各类型自己的obj.toString()方法,如下:

console.log("jerry".toString()); // jerry
console.log((1).toString()); // 1
console.log([1,2].toString()); // 1,2
console.log(new Date().toString()); // Tue Oct 22 2019 15:55:35 GMT+0800 (中国标准时间)
console.log(function(){}.toString()); // function (){}
console.log(null.toString()); // TypeError
console.log(undefined.toString()); // TypeError
console.log({a: 2}.toString()); // [object Object]

 由于undefined和null不是对象,所以它们toString()和valueOf()两个方法都没有,直接调用会报错;

可以对比看出:obj.toString()的结果和Object.prototype.toString.call()的结果不一样!

原因解释:

(1)、toString为Object的原型对象上的方法,而Array 、Function等类型作为Object的实例,都重写了toString方法,在自己的原型对象上;

(2)、不同的对象类型调用toString方法时,根据原型链的知识,调用的是对应的重写之后的toString方法((Function类型返回内容为函数体的字符串,Array类型返回元素组成的字符串.....),而不会去调用Object原型对象上的toString方法(返回对象的具体类型);

(3)、obj.toString()不能得到其对象类型,只能将obj转换为字符串类型;

(4)、因此,在想要得到对象的具体类型时,应该调用Object原型对象上的toString方法;

 接下来我们看一段代码,我们将数组原型对象上的toString方法删除,再执行obj.toString(),如下:

var arr=[1,2,3];
// hasOwnProperty判断对象是否拥有某个属性,不检查原型链
console.log(Array.prototype.hasOwnProperty("toString"));// true
console.log(arr.toString()); // 1,2,3
delete Array.prototype.toString; // delete操作符可以删除自有属性,不能删除继承属性
console.log(Array.prototype.hasOwnProperty("toString")); // false
console.log(arr.toString()); // "[object Array]"

 解释:

(1)、删除了Array的toString()方法后,再调用arr.toString(),此时由于实例本身在对应的原型对象上没找到toString(),会沿着原型链找到Object.prototype.toString(),进而调用它,即arr.toString(),所以返回了和Object.prototype.toString.call(arr)一样的结果;

(2)、使用Object.prototype.toString.call(arr)说白了也是用到了‘借用方法’思想。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值