导航
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
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>
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
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>
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);
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);
;(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);
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);
function Test() {}
const test = new Test();
console.log(test instanceof Test);
console.log(test instanceof Array);
console.log([] instanceof Object);
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));
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; // 返回结果
};
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; // 返回结果
};
Function.prototype.myBind = function(context, ...bindArgs) {
const fn = this; // 保存当前函数
return function(...args) {
return fn.apply(context, bindArgs.concat(args)); // 使用 apply 调用并结合绑定和调用时的参数
};
};
function _new(fn, ...args) {
const obj = Object.create(fn.prototype)
const ret = fn.apply(obj, args)
return ret instanceof Object ? ret : obj
}
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;
}