牛小葵

牛气冲天 一举夺魁

  • 主页
  • JavaScript
  • 读书笔记
  • VUE
所有文章 关于我

牛小葵

牛气冲天 一举夺魁

  • 主页
  • JavaScript
  • 读书笔记
  • VUE

模拟实现call、applay、bind和new

2017-11-13

摘要:学习冴羽的博客模拟实现call、applay、bind 和 new

call

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Function.prototype._call = function (context) {
// 如果不是函数调用就跳出
if (typeof this !== "function") return;
// 当 context 为 null 时指向 window(ES5)
var context = context || window;
// 私有方法指向绑定的方法
context._fn = this;
var args = [];
// 将所有参数添加到 args 中
for (var i = 1; i < arguments.length; i++) {
args.push("arguments[" + i + "]");
}
// arg-->["arguments[1]", "arguments[2]", "arguments[3]"]
// 求返回值,eval() 函数可计算某个字符串,并执行其中的的 js 代码
// 数组拼接字符串:调用 Array.toString()-->"arguments[1],arguments[2],arguments[3]"
var result = eval("context._fn(" + args + ")");
// 删除私有方法
delete context._fn
return result;
}

使用 ES6 更方便实现,主要是处理不定数量的参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Function.prototype._call = function (context) {
// 如果不是函数调用就跳出
if (typeof this !== "function") return;
// 当 context 为 null 时指向 window(ES5)
var context = context || window;
// 私有方法指向绑定的方法
context._fn = this;
// 使用 ES6 语法更好的处理不定数量的参数
var args = [...arguments].slice(1);
var result = context._fn(...args);
// 删除私有方法
delete context._fn
return result;
}

apply

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Function.prototype._apply = function (context, arr) {
var context = context || window;
context._fn = this;
var result;
if (!arr) {
result = context._fn();
} eles {
var args = [];
// arr 是一个数组
for (var i = 0; i < arguments.length; i++) {
args.push("arr[" + i + "]");
}
result = eval("context._fn(" + args + ")");
}
delete context._fn
return result;
}

bind

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Function.prototype._bind = function (context) {
// 如果不是函数来调用 _bind 方法就跳出
if (typeof this !== "function") return
var self = this;
// 做构造函数时会把原函数当成构造器,提供的 this 值被忽略,同时调用时的参数被提供给模拟函数
// args 为第一部分参数,调用 _bind 方法时接收
var args = Array.prototype.slice.call(argumets, 1);
var boundProto = function () { };
var bound = function () {
// bindArgs 为第二部分参数,调用 new 生成实例时接收
var bindArgs = Array.prototype.slice.call(arguments);
// 当作为构造函数时,this 指向实例
// 当作为普通函数时,this 指向 context
self.apply(this instanceof boundProto ? this : context, args.concat(bindArgs))
}
// 在原型链上添加一层空对象,实例可以继承绑定函数原型中的值,同时还可以防止修改绑定函数原型上的值
boundProto.prototype = this.prototype;
bound.prototype = new boundProto();
return bound;
}

new

1
2
3
4
5
6
7
8
9
10
function _new() {
// 第一个参数为构造函数
let Constructor = Array.prototype.shift.call(arguments);
// 以 Constructor.prototype 为原型创建一个新对象 obj
let obj = Object.create(Constructor.prototype);
// 构造函数接收其他参数,并将 this 指向新对象 obj 进行构建
let result = Constructor.apply(obj, arguments);
// 判断构造函数的返回值类型,如果是对象(排除 null)或函数就返回构造函数返回的值 result,否则返回构建的对象 obj。因为使用 new 创建对象有个特性:如果没有返回值或者 return 的是基本数据类型,那么就会返回生成的实例;但如果 return 的是引用类型的对象,那么返回值就是该对象
return result && (typeof result === "object" || typeof result === "function") ? result : obj
}
  • JavaScript
创建对象
Git基础
© 2023 牛小葵
Hexo Theme Yilia by Litten
  • 所有文章
  • 关于我

tag:

  • CSS
  • JavaScript
  • Flex
  • 工具
  • HTML
  • JSON
  • 浏览器
  • node
  • MongoDB
  • Vue
  • 笔记
  • 正则
  • 数据结构与算法
  • underscore
  • 小程序
  • 压缩
  • 总结
  • 目标
  • 设计模式
  • HTTP
  • 【安全】
  • Chrome

    缺失模块。
    1、请确保node版本大于6.2
    2、在博客根目录(注意不是yilia根目录)执行以下命令:
    npm i hexo-generator-json-content --save

    3、在根目录_config.yml里添加配置:

      jsonContent:
        meta: false
        pages: false
        posts:
          title: true
          date: true
          path: true
          text: false
          raw: false
          content: false
          slug: false
          updated: false
          comments: false
          link: false
          permalink: false
          excerpt: false
          categories: false
          tags: true
    

持续学习