导航


HTML

CSS

JavaScript

浏览器 & 网络

版本管理

框架

构建工具

TypeScript

性能优化

软实力

算法

UI、组件库

Node

冷门技能

call

特性

原生

function test(a, b) {
	console.log(this, a, b);
}

function test2() {}
test.call(); // window undefined undefined (严格模式下 this 为 undefined)
test.call(undefined, 1, 2); // window 1 2 (严格模式下传入undefined,test中this是undefined)
test.call({
	name: 'Lance',
  age: 27
}, 1, 2); // obj 1 2
test.call(test2, 1, 2); // test2 1 2
test.call(1, 1, 2); // 1 1 2
test.call(true, 1, 2); // true 1 2

Untitled

实现

const utilsModule = (function(Function) {
	Function.prototype.myCall = function(ctx) {
    // 传了 context 上下文就设置上,没传默认 window
    // 如果传了,就用 Object 包一下(因为有可能传基本类型的值)
  	ctx = ctx ? Object(ctx) : window;
    // 函数谁调用,this指向谁,那我们就把当前this(函数本身)挂载到 ctx 上
    ctx.originFn = this;
    // 从第二个参数开始,都是函数本身要接收的参数了,收集起来
    var args = [];
    for (var i = 1; i < arguments.length; i++) {
    	args.push('arguments[' + i + ']');
    }
    // 使用 eval 执行函数
    var ret = eval('ctx.originFn(' + args + ')');
    // 删除临时方法 originFn
    delete ctx.originFn;
    return ret;
  }
})(Function);

export default utilsModule;

使用:

<!DOCTYPE html>
<html lang="en">
<body>
  <script type="module">
  import utilsModule from './index.js'
  function test(a, b) {
    console.log(this, a, b);
  }
  function test2() {}
  test.myCall(undefined, 1, 2); // window 1 2
  test.myCall({
  	name: 'Lance',
    age: 27
  }, 1, 2); // obj 1 2
  test.myCall(test2, 1, 2); // test2 1 2
  test.myCall(1, 1, 2); // 1 1 2
  test.myCall(true, 1, 2); // true 1 2
  </script>
</body>
</html>

Untitled

apply

特性

原生

function test(a, b) {
  console.log(this, a, b);
}
function test2() {}

test.apply(undefined, [1, 2]); // undefined 1 2
test.apply({
  name: 'Lance',
  age: 27
}, [1, 2]); // obj 1 2
test.apply(test2, [1, 2]); // test2 1 2
test.apply(1, [1, 2]); // 1 1 2
test.apply(1, null); // 1 undefined undefined
test.apply(1, undefined); // 1 undefined undefined
test.apply(1, function() {}); // 1 undefined undefined
test.apply(1, {}); // 1 undefined undefined
test.apply(1, 1); // Uncaught TypeError: CreateListFromArrayLike called on non-object

Untitled

实现

const utilsModule = (function(Function) {
  Function.prototype.myApply = function(ctx, args) {
    var ctx = ctx ? Object(ctx) : window;
    ctx.originFn = this;

    if (typeof args !== 'object' && typeof args !== 'undefined') {
      throw new TypeError('Uncaught TypeError: CreateListFromArrayLike called on non-object');
    }
    if (!args || typeOf(args) !== 'array') {
      return ctx.originFn();
    }
    // 下面执行后 args 跟字符串拼接会默认调用 toString 方法,数组会被默认展开为逗号隔开的数据
    var ret = eval('ctx.originFn(' + args + ')');
    delete ctx.originFn;
    return ret;
  }

  function typeOf(value) {
    // typeof: string、boolean、number、undefined、function、object、bigint、symbol
    var types = {
      '[object Object]': 'object',
      '[object Array]': 'array',
      '[object Number]': 'number',
      '[object String]': 'string',
      '[object Boolean]': 'boolean'
    }
    if (value === null) {
      return 'null';
    } else if (value instanceof Object) {
      return types[Object.prototype.toString.call(value)];
    } else {
      return typeof value;
    }
  }

  return {
  	typeOf
  }
})(Function);

export default utilsModule;

使用:

<!DOCTYPE html>
<html lang="en">
<body>
  <script type="module">
  import utilsModule from './index.js'
  function test(a, b) {
    console.log(this, a, b);
  }
  function test2() {}

	test.myApply(undefined, [1, 2]); // undefined 1 2
  test.myApply({
    name: 'Lance',
    age: 27
  }, [1, 2]); // obj 1 2
  test.myApply(test2, [1, 2]); // test2 1 2
  test.myApply(1, [1, 2]); // 1 1 2
  test.myApply(1, null); // 1 undefined undefined
  test.myApply(1, undefined); // 1 undefined undefined
  test.myApply(1, function() {}); // 1 undefined undefined
  test.myApply(1, {}); // 1 undefined undefined
  test.myApply(1, 1); // Uncaught TypeError: CreateListFromArrayLike called on non-object
  </script>
</body>
</html>

Untitled

bind

特性

原生

function test(user, car) {
  console.log(user + '刚买了一辆' + car + '车');
  console.log(this, arguments);
}
test.prototype.myLove = '李四';

const t = test.bind({
  a: 1,
  b: 2
}, '张三');
t('奔驰');

const newT = new t('奔驰');
console.log(newT);

Untitled

实现

const utilsModule = (function(Function) {
  Function.prototype.myBind = function(ctx) {
    var originFn = this,
        // bind接收的一部分参数(去掉第一个ctx)
        args = [].slice.call(arguments, 1),
        // 原型传递中介函数
        _tempFn = function() {};

    var newFn = function() {
      // 返回的新函数,接收到的后续参数
      var newArgs = [].slice.call(arguments);

      return originFn.apply(
        // 如果 new t,那么this应该指向newFn构造出来的实例,否则指向ctx
        this instanceof newFn ? this : ctx,
        args.concat(newArgs));
    }

    // 圣杯模式继承 把原函数的prototype 交给 中介函数的原型
    _tempFn.prototype = this.prototype;
    // 设置 newFn 的 prototype 为 中介函数的实例对象
    newFn.prototype = new _tempFn();

    return newFn;
  }
})(Function);

export default utilsModule;

使用:

function test(user, car) {
  console.log(user + '刚买了一辆' + car + '车');
  console.log(this, arguments);
}
test.prototype.myLove = '李四';

const t = test.myBind({
  a: 1,
  b: 2
}, '张三');

t('奔驰');

const newT = new t('奔驰');
console.log(newT);

Untitled

new

原生

;(function() {
  function Test(a, b) {
    this.a = a;
    this.b = b;
  }
  Test.prototype.add = function() {
    return this.a + this.b;
  }
  window.Test = Test;
})();

const test = new Test(1, 2);
console.log(test);

Untitled

实现

const utilsModule = (function(Function) {
  function myNew() {
    var constructor = [].shift.call(arguments),
        _this = {};

    _this.__proto__ = constructor.prototype;

    // // 经过上边的 shift ,此时的 arguments 已经把 constructor 剔除了
    const ret = constructor.apply(_this, arguments);

    // 如果构造函数 return 一个复杂类型值,会覆盖原本实例化的this
    return (typeof ret === 'object' && ret !== null)
      		? ret
    			: _this;
  }

  return {
    myNew
  }
})(Function);

export default utilsModule;

或者

function _new(fn, ...args) {
  const obj = Object.create(fn.prototype)
  const ret = fn.apply(obj, args)
  return ret instanceof Object ? ret : obj
}
// 测试
function Person(name, age) {
  this.name = name
  this.age = age
  this.sayHi = function() {}
}
Person.prototype.run = function() {}
console.log(_new(Person, 'Lance', 19))
console.log(new Person('Jerry', 20))

使用:

;(function() {
  function Test(a, b) {
    this.a = a;
    this.b = b;
    // return [];
  }
  Test.prototype.add = function() {
    return this.a + this.b;
  }
  window.Test = Test;
})();

// const test = new Test(1, 2);
const test = utilsModule.myNew(Test, 1, 2);
console.log(test);

Untitled

instanceof

原生

function Test() {}
const test = new Test();
console.log(test instanceof Test);
console.log(test instanceof Array);
console.log([] instanceof Object);

Untitled

实现

function _instanceof (instanceObject, classFunc) {
  let classFunc = classFunc.prototype; // 取得当前类的原型
  let proto = instanceObject.__proto__; // 取得当前实例对象的原型链

  while (true) {
    if (proto === null) { // 找到了 Object的基类 Object.prototype.__proto__
      return false;
    };
    if (proto === classFunc) { // 在当前实例对象的原型链上,找到了当前类
      return true;
    }
    proto = proto.__proto__; // 沿着原型链__ptoto__一层一层向上查找
  }
}

或者

function myInstanceOf(instance, constructor) {
  var left = instance, // 左边是实例
      right = constructor, // 右边是构造函数
      proto = left.__proto__; // 拿到实例的原型
  while(proto) {
    if (proto === right.prototype) {
      return true;
    }
    // 没找到,就向上再找
    proto = proto.__proto__;
  }
  // 找到头都没找到,就是false
  return false;
}

使用:

function Test() {}
const test = new Test();
console.log(utilsModule.myInstanceOf(test, Test));
console.log(utilsModule.myInstanceOf(test, Array));
console.log(utilsModule.myInstanceOf([], Object));

Untitled

简易版

call

Function.prototype.myCall = function(context, ...args) {
  context = context ? Object(context) : globalThis;  // 如果未提供 context,使用全局对象
  const fnSymbol = Symbol();  // 防止属性覆盖
  context[fnSymbol] = this;   // 将当前函数赋给 context 上的临时属性
  const result = context[fnSymbol](...args);  // 调用该函数,传入剩余参数
  delete context[fnSymbol];  // 删除临时属性
  return result;  // 返回结果
};

apply

Function.prototype.myApply = function(context, args) {
  context = context ? Object(context) : globalThis;  // 如果未提供 context,使用全局对象
  const fnSymbol = Symbol();  // 防止属性覆盖
  context[fnSymbol] = this;   // 将当前函数赋给 context 上的临时属性
  const result = context[fnSymbol](...args);  // 调用该函数,使用展开运算符传入参数
  delete context[fnSymbol];  // 删除临时属性
  return result;  // 返回结果
};

bind

Function.prototype.myBind = function(context, ...bindArgs) {
  const fn = this;  // 保存当前函数
  return function(...args) {
    return fn.apply(context, bindArgs.concat(args));  // 使用 apply 调用并结合绑定和调用时的参数
  };
};

new

function _new(fn, ...args) {
  const obj = Object.create(fn.prototype)
  const ret = fn.apply(obj, args)
  return ret instanceof Object ? ret : obj
}

instanceof

function myInstanceOf(instance, constructor) {
  var left = instance, // 左边是实例
      right = constructor, // 右边是构造函数
      proto = left.__proto__; // 拿到实例的原型
  while(proto) {
    if (proto === right.prototype) {
      return true;
    }
    // 没找到,就向上再找
    proto = proto.__proto__;
  }
  // 找到头都没找到,就是false
  return false;
}