Function原型链中的 applycallbind 方法是 JavaScript 中相当重要的概念,与 this 关键字密切相关,相当一部分人对它们的理解还是比较浅显,所谓js基础扎实,绕不开这些基础常用的API,这次让我们来了解它们吧!

实现new运算符

原理

new 关键字会进行如下的操作:

  1. 创建一个空的简单JavaScript对象(即{});
  2. 链接该对象(设置该对象的constructor)到另一个对象 ;
  3. 将步骤1新创建的对象作为this的上下文 ;
  4. 如果该函数没有返回对象,则返回this

实现代吗

function _new(fn, ...args) {
if (typeof fn !== "function") {
throw TypeError("fn is not");
}
const obj = Object.create(fn.prototype);
const res = fn.call(obj, ...args);
const isObject = typeof res === "object" && res !== null;
return isObject ? res : obj;
}

实现bind方法

语法

function.bind(thisArg[, arg1[, arg2[, ...]]])

参数

thisArg

调用绑定函数时作为 this 参数传递给目标函数的值。 如果使用new运算符构造绑定函数,则忽略该值。当使用 bindsetTimeout 中创建一个函数(作为回调提供)时,作为 thisArg 传递的任何原始值都将转换为 object。如果 bind 函数的参数列表为空,或者thisArgnullundefined,执行作用域的 this 将被视为新函数的 thisArg

arg1, arg2, ...

当目标函数被调用时,被预置入绑定函数的参数列表中的参数。

返回值

返回一个原函数的拷贝,并拥有指定的 this 值和初始参数。

原理

bind() 函数会创建一个新的绑定函数(bound function,BF)。绑定函数是一个 exotic function object(怪异函数对象,ECMAScript 2015 中的术语),它包装了原函数对象。调用绑定函数通常会导致执行包装函数。

绑定函数具有以下内部属性:

  • [[BoundTargetFunction]] - 包装的函数对象
  • [[BoundThis]] - 在调用包装函数时始终作为 this 值传递的值。
  • [[BoundArguments]] - 列表,在对包装函数做任何调用都会优先用列表元素填充参数列表。
  • [[Call]] - 执行与此对象关联的代码。通过函数调用表达式调用。内部方法的参数是一个this值和一个包含通过调用表达式传递给函数的参数的列表。

当调用绑定函数时,它调用 [[BoundTargetFunction]] 上的内部方法 [[Call]],就像这样 Call(boundThis, args)。其中,boundThis 是 [[BoundThis]],args 是 [[BoundArguments]] 加上通过函数调用传入的参数列表。

代码

有两种实现bind的方法,下面第一种不支持使用new调用新创建的构造函数,而第二种支持。

方法一

Function.prototype.myBind = function () {
const thatFunc = this,
thatArg = arguments[0],
slice = Array.prototype.slice;
let args = slice.call(arguments, 1);
if (typeof thatFunc !== "function") {
throw new TypeError(
"Function.prototype.bind - what is trying to be bound is not callable"
);
}
return function () {
var funcArgs = args.concat(slice.call(arguments));
return thatFunc.apply(thatArg, funcArgs);
};
};

方法二

Function.prototype.myBind = function (otherThis) {
if (typeof this !== "function") {
throw new TypeError(
"Function.prototype.bind - what is trying to be bound is not callable"
);
}
const ArrayPrototypeSlice = Array.prototype.slice;
let baseArgs = ArrayPrototypeSlice.call(arguments, 1),
baseArgsLength = baseArgs.length,
fToBind = this,
fNOP = function () {},
fBound = function () {
baseArgs.length = baseArgsLength; // reset to default base arguments
baseArgs.push.apply(baseArgs, arguments);
return fToBind.apply(
fNOP.prototype.isPrototypeOf(this) ? this : otherThis,
baseArgs
);
};
if (this.prototype) {
fNOP.prototype = this.prototype;
}
fBound.prototype = new fNOP();
return fBound;
};

实现call方法

语法

func.call([thisArg[, arg1, arg2, ...argN]])

参数

thisArg

可选的。在 function 函数运行时使用的 this 值。请注意,this可能不是该方法看到的实际值:如果这个函数处于非严格模式下,则指定为 null 或 undefined 时会自动替换为指向全局对象,原始值会被包装。

arg1, arg2, ...

指定的参数列表。

返回值

使用调用者提供的 this 值和参数调用该函数的返回值。若该方法没有返回值,则返回 undefined

实现代码

Function.prototype.myCall = function (ctx, ...arg) {
if (ctx === null || ctx === undefined) {
ctx = window
} else if (typeof ctx !== 'object' || typeof ctx !== 'function') {
ctx = Object(ctx)
}
const tmp = Symbol('tmp');
ctx[tmp] = this;
const result = ctx[tmp](...arg);
delete ctx[tmp];
return result;
};

apply方法的实现

注意:apply()方法的作用和 call() 方法类似,区别就是call()方法接受的是参数列表,而apply()方法接受的是一个参数数组。

Function.prototype.myApply = function (ctx, arg) {
if (ctx === null || ctx === undefined) {
ctx = window
} else if (typeof ctx !== 'object' || typeof ctx !== 'function') {
ctx = Object(ctx)
}
const tmp = Symbol('tmp');
ctx[tmp] = this;
const result = ctx[tmp](...arg);
delete ctx[tmp];
return result;
};

参考书籍

JavaScript中的new,bind,call,apply的简易实现的更多相关文章

  1. 理解JavaScript中的arguments,callee,caller,apply

    arguments 该对象代表正在执行的函数和调用它的函数的参数. [function.]arguments[n] 参数function :选项.当前正在执行的 Function 对象的名字. n : ...

  2. [转] 理解 JavaScript 中的 Array.prototype.slice.apply(arguments)

    假如你是一个 JavaScript 开发者,你可能见到过 Array.prototype.slice.apply(arguments) 这样的用法,然后你会问,这么写是什么意思呢? 这个语法其实不难理 ...

  3. JavaScript中callee与caller,apply与call解析

    1. arguments.callee 1.1 解释 返回正被执行的 Function 对象,也就是所指定的 Function 对象的正文. 1,.2 说明 callee 属性的初始值就是正被执行的 ...

  4. javascript中函数的call,apply及bind方法

    call 方法调用一个对象的一个方法,以另一个对象替换当前对象.call([thisObj[,arg1[, arg2[,  [,.argN]]]]])参数thisObj可选项.将被用作当前对象的对象. ...

  5. javascript中this、new、apply和call详解

    如果在javascript语言里没有通过new(包括对象字面量定义).call和apply改变函数的this指针,函数的this指针都是指向window的,重要的话要说三遍.... 讲解this指针的 ...

  6. javascript 中的 arguments,callee.caller,apply,call 区别

    记录一下: 1.arguments是一个对象, 是函数的一个特性,只有在函数内才具有这个特性,在函数外部不用使用. 举例: function test(){   alert(typeof argume ...

  7. Javascript中call函数和apply函数的使用

    Javascript 中call函数和apply的使用: Javascript中的call函数和apply函数是对执行上下文进行切换,是将一个函数从当前执行的上下文切换到另一个对象中执行,例如: so ...

  8. 深入浅出 妙用Javascript中apply、call、bind

    apply.call 在 javascript 中,call 和 apply 都是为了改变某个函数运行时的上下文(context)而存在的,换句话说,就是为了改变函数体内部 this 的指向. Jav ...

  9. 【优雅代码】深入浅出 妙用Javascript中apply、call、bind

    这篇文章实在是很难下笔,因为网上相关文章不胜枚举. 巧合的是前些天看到阮老师的一篇文章的一句话: “对我来说,博客首先是一种知识管理工具,其次才是传播工具.我的技术文章,主要用来整理我还不懂的知识.我 ...

  10. Javascript中call,apply,bind方法的详解与总结

    在 javascript之 this 关键字详解 文章中,谈及了如下内容,做一个简单的回顾: 1.this对象的涵义就是指向当前对象中的属性和方法. 2.this指向的可变性.当在全局作用域时,thi ...

随机推荐

  1. HQYJ嵌入式学习笔记——C语言复习day2

    1.计算机的数值表示 数值类型和非数值类型 二进制 0,1 (0b1001) 八进制 0~7   (0146) 十进制 0~9 十六进制 0~f (0x3f) 八进制转二进制-->一位八进制数换 ...

  2. JS中try catch的用法

    在js中也可以使用try/catch语法,把可能发生异常的代码使用try包裹起来,然后在catch中对异常进行处理,处理后就不会影响后面代码的执行. const a = null try { cons ...

  3. redis.conf 配置说明

    redis.conf 配置项说明如下: 1. Redis默认不是以守护进程的方式运行,可以通过该配置项修改,使用yes启用守护进程 daemonize no 2. 当Redis以守护进程方式运行时,R ...

  4. Hexo一键部署到阿里云OSS并设置浏览器缓存

    自建博客地址:https://bytelife.net,欢迎访问! 本文为博客自动同步文章,为了更好的阅读体验,建议您移步至我的博客 本文作者: Jeffrey 本文链接: https://bytel ...

  5. 【HTB系列】 靶机Swagshop的渗透测试详解

    出品|MS08067实验室(www.ms08067.com) 本文作者:是大方子(Ms08067实验室核心成员) 总结与反思 使用vi提权 magento漏洞的利用 magescan 工具的使用 靶机 ...

  6. AntDesign Pro + .NET Core 实现基于JWT的登录认证

    很多同学说AgileConfig的UI实在是太丑了.我想想也是的,本来这个项目是我自己使用的,一开始甚至连UI都没有,全靠手动在数据库里修改数据.后来加上了UI也是使用了老掉牙的bootstrap3做 ...

  7. HDOJ-6681(离散化+线段树)

    Rikka With Cake HDOJ-6681 最终的答案为射线的交点数加一.当然,我们也可以证明.证明需要用到欧拉公式 V−E+F=2 V-E+F=2V−E+F=2 .设射线的交点共 c cc ...

  8. Linux:使用systemd管理进程

    Blog:博客园 个人 概述 systemd是目前Linux系统上主要的系统守护进程管理工具,由于init一方面对于进程的管理是串行化的,容易出现阻塞情况,另一方面init也仅仅是执行启动脚本,并不能 ...

  9. 我与FreeBSD的故事之三

    联想G400 是我在国美电器线下买的笔记本.我什么也不懂,就随便买了,不随便也不行,谁都知道只要不是那种特别的奸商,基本上货物都是符合价值决定价格这个基本的经济学规律的.所以没钱就失去了选择的自由.到 ...

  10. FreeBSD ports 基本用法

    首先获取portsnap#portsnap fetch extract---------------------------------------使用whereis 查询软件地址如#whereis ...