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. Scrapy 项目:腾讯招聘

    目的: 通过爬取腾讯招聘网站(https://careers.tencent.com/search.html)练习Scrapy框架的使用 步骤: 1.通过抓包确认要抓取的内容是否在当前url地址中,测 ...

  2. 微信小程序:添加全局的正在加载中图标效果

    在发送请求的时候,显示一个正在加载中的小图标.在加载下一页的时候也显示正在加载中.同时数据请求回来了,把加载中进行关闭. 开发----API-----界面 在哪里添加这两段代码会比较方便呢?一个项目有 ...

  3. WPF -- 一种添加静态资源的方式

    本文介绍使用独立的xaml文件添加静态资源的方式. 步骤 创建XAML文件,如ImageButton.xaml,添加ResourceDictionary标签,并添加静态资源: 在App.xaml的Ap ...

  4. 后端程序员之路 29、Thrift

    Apache Thrift是Facebook实现的一个高效的.支持多种编程语言的远程服务调用(RPC)框架. Apache Thrift - Homehttp://thrift.apache.org/ ...

  5. PAT-1144(The Missing Number)set的使用,简单题

    The Missing Number PAT-1144 #include<iostream> #include<cstring> #include<string> ...

  6. Mybatis系列全解(六):Mybatis最硬核的API你知道几个?

    封面:洛小汐 作者:潘潘 2020 年的大疫情,把世界撕成几片. 时至今日,依旧人心惶惶. 很庆幸,身处这安稳国, 兼得一份安稳工. · 东家常讲的一个词:深秋心态 . 大势时,不跟风.起哄, 萧条时 ...

  7. 阿里的Easyexcel读取Excel文件(最新版本)

      本篇文章主要介绍一下使用阿里开源的Easyexcel工具处理读取excel文件,因为之前自己想在网上找一下这个简单的立即上手的博客,发现很多文章的教程都针对比较旧的版本的Easyexcel,没有使 ...

  8. 为什么要从 Linux 迁移到 BSD 5

    为什么要从 Linux 迁移到 BSD 5 干净的分离 在 FreeBSD 的设计方式下,不同的组件组合在一起的,处理配置和调优,以及多年来开发和改进的所有工具,使得使用 FreeBSD 是一件很特别 ...

  9. 001-HashMap源码分析

    HashMap源码分析 哈希表(hash table)也叫散列表,是一种非常重要的数据结构,应用场景及其丰富,许多缓存技术(比如 memcached)的核心其实就是在内存中维护一张大的哈希表. 一.什 ...

  10. (数据科学学习手札112)Python+Dash快速web应用开发——表单控件篇(上)

    本文示例代码已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 这是我的系列教程Python+Dash快速web ...