js call 用处

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了js call 用处相关的知识,希望对你有一定的参考价值。

javascript 中call用处不少,用一句话概括就是动态改变this.比如说:

function cat()

//做一个原型扩展
cat.prototype=
food:"fish",
say: function()
alert("I love "+this.food);


var blackCat = new cat;
blackCat.say();
//当我需要一条黑狗也说它喜欢什么时:
blackDog = food:"bone";
//我们不想对它重新定义say方法,那么我们可以通过call用blackCat的say方法:
blackCat.say.call(blackDog);
//例子来源于知乎

call用来改变this的,改变的方式就一下几种:

1,上例子中属于继承式

2,替换式

function NameShowing()  
    this.showName = function()  
        document.write(this.name);  
      
  
  
function Person(name)  
    this.name = null;  
    this.Init = function(name)    
        this.name = name;    
        
    this.Init(name);    
;  
  
var nameShowing = new NameShowing();  
var jeremy = new Person("Jeremy")  
  
//替换this指向 jeremy
nameShowing.showName.call(jeremy);

3,实例继承

function NameShowing()  
    this.showName = function()  
        document.write(this.name);  
      
  
  
function Person(name)  
    this.name = null;  
    this.Init = function(name)    
        this.name = name;    
        
    this.Init(name);    
;  
  
var jeremy = new Person("Jeremy")   
NameShowing.call(jeremy);  
jeremy.showName();

2和3一个是针对方法,一个是对象,本质一样。

4,带有构造函数的参数

function Person(name, age)  
    this.name = null;  
    this.age = null;  
    this.showPersonInfo = function()  
        document.write("Name: " + this.name + "<br>");  
        document.write("Age: " + this.age + "<br>");  
    ;  
    this.Init = function()  
        this.name = name;  
        this.age = age;  
    ;  
    this.Init();  
  
  
var jeremy = new Object();  
Person.call(jeremy, "Jeremy", 20);  
jeremy.showPersonInfo();

 参考资料:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call

参考技术A

call用来调用另一个对象的方法,但调用的对象是可以作为参数传入的。

function A(name)//A 类
    this.name=name;
    this.showName=function()
        alert(this.name);
    


function B(name,age) //B 类
    this.name=name;
    this.age=age;

var a=new A("aaa"); //a对象是有showName方法的
var b=new B('bbb',10);//b对象没有showName方法
a.showName.call(b);//但是这里showName,显示的是b的名字,也就是b调用了a的showName方法

call最经典的应用就是继承。javascript是没有对象继承概念的,我们只能用一些方法实现。

function A(name)//A 类 还是这个例子,A有showName
    this.name=name;
    this.showName=function()
        alert(this.name);
    


function B(name,age) //B 类,

    //B类中通过A.call,相当于执行了A(name),但在A中所有this其实都是B。那么B就有name这个属性,同时也拥有showName方法了。
    A.call(this,name);
    this.age=age;

var b=new B('bbb',10);
b.showName();//B继承了A,所以b也有showName方法。

面试必备!JS:call详解以及自己手写call

注:本篇文章示例来源于MDN:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/call

目录

call详解

 call的定义

 call的语法

 call的描述(来源MDN)

 call的示例(干货来了)

使用 call 方法调用父构造函数

使用 call 方法调用匿名函数

使用 call 方法调用函数并且指定上下文的 'this'

使用 call 方法调用函数并且不指定第一个参数(argument)

自己实现call方法


不知道各位是否看到大佬写的代码,里面的代码很是简洁,但是this出现的很多,上下文的调用,this的指向我们被绕的头晕,但是大佬却信手拈来,我觉得其中call的作用功不可没,可见想要js进阶,精通call的使用是不可或缺的。那就让我们来解开call的神秘面纱吧。

call详解


 call的定义

       PS(很多官方资料叙述的call都是有些抽象的,因为他们必须保证他们语言的严谨性和准确性,但是我就不一样了,咱也不是官方,咱只需要叙述的明白,大家里脊即可。)

       我的理解,call就是为了将本身this指向改变为新对象的值。无论是继承还是添加属性,都是利用了其this指向的变化而起作用的。

       之后我们会通过MDN的几种示例来一一验证。我们先看看它的通用语法。有不理解的不要紧,往下看就行。

 call的语法

function.call(thisArg, arg1, arg2, ...)
  1. function就是个方法,我们通过一个类型是function的对象调用call
  2. thisArg代表this值,但是这个this值是在function运行时的this值。(反复读,还不理解就向下看,有例子阐述)非严格模式,null和undefined会被替换为全局对象。
  3. arg1,arg2,...参数列表
  4. 其返回值来源于function最终的返回值,若没有则为undefined

 call的描述(来源MDN)

  call() 允许为不同的对象分配和调用属于一个对象的函数/方法。

  call() 提供新的 this 值给当前调用的函数/方法。你可以使用 call 来实现继承:写一个方法,然后让另外一个新的对象来继承它(而不是在新对象中再写一次这个方法)。

 call的示例(干货来了)

    以下示例很是经典,建议手敲,并在不理解的地方看一看其打印值。

    先看代码,再看我的解释,将你的理解和我的理解对比一下,有问题我们评论区探讨或加下方微信

    我们看看下面的代码,这一段代码的目的是给Food构造函数和Toy的属性值赋值,仅通过一个Project的父构造函数,就能将两个不同类型的子构造函数Food和Toy其相同属性赋值,大大减少了冗余代码

   Food和Toy构造函数通过call继承了Project构造函数。大家回想下我最开始说的,call就是改变了其this指向。我们按照代码的执行顺序来看。

  1. 先是定义了Project、Food、Toy构造函数。
  2. 调用Food、Toy构造函数
  3. 重点:  Product.call(this, name, price);  首先明确一点,这里这个this值为Food构造函数,没什么争议的。将其对应到语法中也就是thisArg就是Food构造函数,function就是Project构造函数,则Project的运行时的this值就为Food构造函数。

大家运行后可以看看几个构造函数中的this值是什么,调用call之前和之后的this值变化。

function Product(name, price) {
  console.log('Project构造函数this:',this)
  this.name = name;
  this.price = price;
}

function Food(name, price) {
  console.log('Food构造函数this:',this)
  Product.call(this, name, price);
  this.category = 'food';
}

function Toy(name, price) {
  console.log('Toy构造函数this:',this)
  Product.call(this, name, price);
  this.category = 'toy';
}

var cheese = new Food('feta', 5);
var fun = new Toy('robot', 40);

下面这段代码,定义了一个匿名函数,该匿名函数调用call,为其animal中的对象增加print方法,并调用。

分析了一个例子,这个就不用那么细了。我们直奔主题。

对比语法,匿名函数就是语法中的function,animals[i],就是该匿名函数中this的值,因此通过循环我们为animals的每个对象增加了print方法。

var animals = [
  { species: 'Lion', name: 'King' },
  { species: 'Whale', name: 'Fail' }
];

for (var i = 0; i < animals.length; i++) {
  //匿名函数
  (function(i) {
    this.print = function() {
      console.log('#' + i + ' ' + this.species
                  + ': ' + this.name);
    }
    this.print();
  }).call(animals[i], i);//调用call
}

如果不通过greet.call调用,greet函数中的this指向的是greet函数本身,则this.animal则undefined

依旧是改变this指向。

function greet() {
  var reply = [this.animal, '通常睡', this.sleepDuration].join(' ');
  console.log(reply);
}

var obj = {
  animal: '猫', sleepDuration: '12到16个小时'
};

greet.call(obj);  //其greet函数中的this值为obj

非严格模式下,若不定义call函数中第一个参数thisArg值,则thisArg值为window(全局对象)

严格模式下,若不定义call函数中第一个参数thisArg值,则thisArg值为undefined

var sData = 'Wisen';

function display() {
  console.log('sData value is %s ', this.sData);
}

display.call();  // sData value is Wisen
'use strict';

var sData = 'Wisen';

function display() {
  console.log('this',this);
  console.log('sData value is %s ', this.sData);
}

display.call(); 
// this undefined
// Cannot read the property of 'sData' of undefined

自己实现call方法

  下面的代码,基本实现了call方法的作用,一些异常还没做。留给各位小伙伴了。

  call的主要目的是将function中的this指向变为thisArg,因此我们只需要将调用call时的this赋给thisArg就好了。

代码分四步:

  1. 解析出参数列表
  2. 改变function的this指向为thisArg
  3. 释放额外赋予的对象属性
  4. 返回结果
Function.prototype.callFn = function(context) {
  //thisArg若为undefined或null,将其设为window全局
  if(context === undefined || context ===null){
    context = window
  }
  //将参数从arguments取出
  var args = []
  for (let i=0;i<arguments.length;i++) {
    if(i!== 0){
      args.push(arguments[i])
    }
  }

  //console.log('callFn_this:',this)//this为Project
  //console.log('callFn_context:',context)//context为Food
  //将context设置为对象,并将this给予context任意一个属性
  context = new Object(context)
  const key = 'key'
  context[key] = this

  //返回结果为thisArg(...args)
  const result = context[key](...args)
  delete context[key]
  return result
}

测试代码:

不做解释了就

Function.prototype.callFn = function(context) {
  //thisArg若为undefined或null,将其设为window全局
  if(context === undefined || context ===null){
    context = window
  }
  //将参数从arguments取出
  var args = []
  for (let i=0;i<arguments.length;i++) {
    if(i!== 0){
      args.push(arguments[i])
    }
  }

  //console.log('callFn_this:',this)//this为Project
  //console.log('callFn_context:',context)//context为Food
  //将context设置为对象,并将this给予context任意一个属性
  context = new Object(context)
  const key = 'key'
  context[key] = this

  //返回结果为thisArg(...args)
  const result = context[key](...args)
  delete context[key]
  return result
}

function Product(name, price) {
  console.log('Product this:',this)
  this.name = name;
  this.price = price;
}

function Food(name, price) {
  console.log('Food_this:',this)
  let newFood = Product.callFn(this, name, price);
  // let newFood = Product.apply(this, [name, price]);
  // let newFood = Product.bind(this, [name, price])();
  console.log(this === newFood)
  this.category = 'food';
}
console.log(new Food('cheese', 5).name);

 

以上是关于js call 用处的主要内容,如果未能解决你的问题,请参考以下文章

js中的call

js中call和apply的区别

面试必备!JS:call详解以及自己手写call

面试必备!JS:call详解以及自己手写call

面试必备!JS:call详解以及自己手写call

JS中 call和apply的区别和作用