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的定义

1 PS(很多官方资料叙述的call都是有些抽象的,因为他们必须保证他们语言的严谨性和准确性,但是我就不一样了,咱也不是官方,咱只需要叙述的明白,大家里脊即可。) 2 3 我的理解,**call就是为了将本身this指向改变为新对象的值**。无论是继承还是添加属性,都是利用了其this指向的变化而起作用的。 4 5 之后我们会通过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的示例(干货来了)

1以下示例很是经典,建议手敲,并在不理解的地方看一看其打印值。 2 3先看代码,再看我的解释,将你的理解和我的理解对比一下,有问题我们评论区探讨或加下方微信
  • 使用 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值变化。

1function Product(name, price) { 2 console.log('Project构造函数this:',this) 3 this.name = name; 4 this.price = price; 5} 6 7function Food(name, price) { 8 console.log('Food构造函数this:',this) 9 Product.call(this, name, price); 10 this.category = 'food'; 11} 12 13function Toy(name, price) { 14 console.log('Toy构造函数this:',this) 15 Product.call(this, name, price); 16 this.category = 'toy'; 17} 18 19var cheese = new Food('feta', 5); 20var fun = new Toy('robot', 40);

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

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

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

1var animals = [ 2 { species: 'Lion', name: 'King' }, 3 { species: 'Whale', name: 'Fail' } 4]; 5 6for (var i = 0; i < animals.length; i++) { 7 //匿名函数 8 (function(i) { 9 this.print = function() { 10 console.log('#' + i + ' ' + this.species 11 + ': ' + this.name); 12 } 13 this.print(); 14 }).call(animals[i], i);//调用call 15}

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

依旧是改变this指向。

1function greet() { 2 var reply = [this.animal, '通常睡', this.sleepDuration].join(' '); 3 console.log(reply); 4} 5 6var obj = { 7 animal: '猫', sleepDuration: '12到16个小时' 8}; 9 10greet.call(obj); //其greet函数中的this值为obj

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

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

1var sData = 'Wisen'; 2 3function display() { 4 console.log('sData value is %s ', this.sData); 5} 6 7display.call(); // sData value is Wisen
1'use strict'; 2 3var sData = 'Wisen'; 4 5function display() { 6 console.log('this',this); 7 console.log('sData value is %s ', this.sData); 8} 9 10display.call(); 11// this undefined 12// Cannot read the property of 'sData' of undefined

自己实现call方法

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

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

代码分四步:

  1. 解析出参数列表
  2. 改变function的this指向为thisArg
  3. 释放额外赋予的对象属性
  4. 返回结果
1Function.prototype.callFn = function(context) { 2 //thisArg若为undefined或null,将其设为window全局 3 if(context === undefined || context ===null){ 4 context = window 5 } 6 //将参数从arguments取出 7 var args = [] 8 for (let i=0;i<arguments.length;i++) { 9 if(i!== 0){ 10 args.push(arguments[i]) 11 } 12 } 13 14 //console.log('callFn_this:',this)//this为Project 15 //console.log('callFn_context:',context)//context为Food 16 //将context设置为对象,并将this给予context任意一个属性 17 context = new Object(context) 18 const key = 'key' 19 context[key] = this 20 21 //返回结果为thisArg(...args) 22 const result = context[key](...args) 23 delete context[key] 24 return result 25}

测试代码:

不做解释了就

1Function.prototype.callFn = function(context) { 2 //thisArg若为undefined或null,将其设为window全局 3 if(context === undefined || context ===null){ 4 context = window 5 } 6 //将参数从arguments取出 7 var args = [] 8 for (let i=0;i<arguments.length;i++) { 9 if(i!== 0){ 10 args.push(arguments[i]) 11 } 12 } 13 14 //console.log('callFn_this:',this)//this为Project 15 //console.log('callFn_context:',context)//context为Food 16 //将context设置为对象,并将this给予context任意一个属性 17 context = new Object(context) 18 const key = 'key' 19 context[key] = this 20 21 //返回结果为thisArg(...args) 22 const result = context[key](...args) 23 delete context[key] 24 return result 25} 26 27function Product(name, price) { 28 console.log('Product this:',this) 29 this.name = name; 30 this.price = price; 31} 32 33function Food(name, price) { 34 console.log('Food_this:',this) 35 let newFood = Product.callFn(this, name, price); 36 // let newFood = Product.apply(this, [name, price]); 37 // let newFood = Product.bind(this, [name, price])(); 38 console.log(this === newFood) 39 this.category = 'food'; 40} 41console.log(new Food('cheese', 5).name);
点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

JavaScript 笛卡尔积算法,可用于商品 SKU 计算

代码示例:Demo演示:jsfunctioncalcDescartes(array){if(array.length<2)returnarray0||;return.reduce.call(array,function(col,set){varres;

JavaScript中call()、apply()、bind()的用法

call()apply()bind()都是用来更改this的指向的其中bind()返回的是一个函数,必须执行才行传参差异:call、bind、apply这三个函数的第一个参数都是this的指向对象,第二个参数差别就来了:call的参数是直接放进去的,第二第三第n个参数全都用逗号分隔,直接放到后面obj.myFun.call(db,'

bind、call、apply 区别?如何实现一个bind?

一、作用call、apply、bind作用是改变函数执行时的上下文,简而言之就是改变函数运行时的this指向那么什么情况下需要改变this的指向呢?下面举个例子var name"lucy";const obj{    name:"martin",    say:function (){        co