/**
* New手写
* 用new Object() 的方式新建了一个对象 obj
取出第一个参数,就是我们要传入的构造函数。此外因为 shift 会修改原数组,所以 arguments 会被去除第一个参数
将 obj 的原型指向构造函数,这样 obj 就可以访问到构造函数原型中的属性
使用 apply,改变构造函数 this 的指向到新建的对象,这样 obj 就可以访问到构造函数中的属性
返回 obj
考虑构造函数又返回值的情况:
如果构造函数返回一个对象,那么我们也返回这个对象
如上否则,就返回默认值
*/
export function objectFactory() {
var obj = new Object(),//从Object.prototype上克隆一个对象
Constructor = [].shift.call(arguments);//取得外部传入的构造器
var F = function () { };
F.prototype = Constructor.prototype;
obj = new F();//指向正确的原型
var ret = Constructor.apply(obj, arguments);//借用外部传入的构造器给obj设置属性
return typeof ret === 'object' ? ret : obj;//确保构造器总是返回一个对象
}
/**
* 手写call
* call 改变了 this 的指向,指向到 传进来的对象;执行传进来的函数
* 实现思路:1. 将函数设为对象的属性;2. 执行该函数;3. 删除该函数
* call 函数还能给定参数执行函数
*/
Function.prototype.myCall = function (context) {
var context = context || window; // this 参数可以传 null,当为 null 的时候,视为指向 window
context.fn = this; // 首先要获取调用call的函数,用this可以获取
// call 函数还能给定参数执行函数
// 因为arguments是类数组对象,所以可以用for循环
var args = [];
for (var i = 0, len = arguments.length; i < len; i++) {
args.push(`arguments[${i}]`)
}
// 我怎么将args这些参数传进去,args.join(',')?不行啦
var result = eval(`context.fn(${args})`); // eval拼成一个函数会自己执行,args 会自动调用 Array.toString() 这个方法。
delete context.fn;
return result; // 函数是可以有返回值的!
}
/**
* 手写apply(方法一)
* arr:数组 [a,b,...]
*/
Function.prototype.myApply = function(context, arr){
context = Object(context) || window;
context.fn = this;
var result;
if(!arr){
result = context.fn;
}else{
var arrTmp = [];
for(var i = 0, len = arr.length; i<len; i++){
arrTmp.push(`arr[${i}]`);
}
result = eval(`context.fn(${arrTmp})`)
}
delete context.fn;
return result;
}
/**
* 手写bind
* bind() 函数会创建一个新函数(称为绑定函数)
* 是在ie8下,bind使用不了,写下兼容版
*/
if (!Function.prototype.bind) {
Function.prototype.bind = function (oThis) {
if (typeof this !== "function") {
// closest thing possible to the ECMAScript 5
// internal IsCallable function
throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this, // this在这里指向的是目标函数
fNOP = function () {},
fBound = function () {
return fToBind.apply(this instanceof fNOP
? this //此时的this就是new出的obj
: oThis || this,//如果传递的oThis无效,就将fBound的调用者作为this
//将通过bind传递的参数和调用时传递的参数进行合并,并作为最终的参数传递
aArgs.concat(Array.prototype.slice.call(arguments)));
};
fNOP.prototype = this.prototype;
//将目标函数的原型对象拷贝到新函数中,因为目标函数有可能被当作构造函数使用
fBound.prototype = new fNOP();
//返回fBond的引用,由外部按需调用
return fBound;
};
}
/**
* Promise手写(https://www.jianshu.com/p/89f6409a7936)
* 创建三变量记录表示状态
* 用that保存this,避免后期闭包导致this的指向不对
* value 变量用于保存 resolve 或者 reject 中传入的值
* resolvedCallbacks 和 rejectedCallbacks 用于保存 then 中的回调,
* 因为当执行完 Promise 时状态可能还是等待中,这时候应该把 then 中的回调保存起来用于状态改变时使用
*/
//先创建三个常量用于表示状态
const PENDING = 'pending', RESOLVED = 'resolved', REJECTED = 'rejected'
export function HandleWritePromise(fn) {
const that = this
that.state = PENDING //一开始Promise转态应该是pedding
that.value = null
that.resolvedCallbacks = []
that.rejectedCallbacks = []
function resolved(value) {
if (that.state === PENDING) {
that.state = RESOLVED
that.value = value
//执行回调方法
that.resolvedCallbacks.map(cb => cb(that.value))
}
}
function rejected(value) {
if (that.state === REJECTED) {
that.state = REJECTED
that.value = value
//执行回调方法
that.rejectedCallbacks.map(cb => cb(that.value))
}
}
//执行回调方法
try {
fn(resolved, rejected)
} catch (e) {
rejected(e)
}
}
//实现then函数
HandleWritePromise.prototype.then = function (onFullfied, onRejected) {
const that = this
onFullfied = typeof onFullfied === 'function' ? onFullfied : v => v
onRejected = typeof onRejected === 'function' ? onRejected : r => {
throw r
}
//等待状态,则添加回调函数到栈中
if (that.state === PENDING) {
that.resolvedCallbacks.push(onFullfied)
that.rejectedCallbacks.push(onRejected)
}
if (that.state === RESOLVED) {
onFullfied(that.value)
}
if (that.state === REJECTED) {
onRejected(that.value)
}
}