1.重构new
function Fn() {
/*
1.要有一个对象
// 方案1
let obj = {}
obj.__proto__ = FC.prototype
// 方案2
let obj = Object.create(FC.prototype)
2.执行此函数,解决this的指向 let ret = FC.apply(obj)
3.判断函数执行的返回值,是基本类型或无返回值则返回对象,有引用类型,则返回指定的
*/
}
let f1 = new Fn()
// ===============================================================
function _new(Construct, ...args) {
// 创建一个实例对象(创建Construct类的实例,让其 对象.__proto__ = Construct.prototype)
// obj.__proto__ = Construct.prototype
let obj = Object.create(Construct.prototype)
// 把函数执行,让this指向实例对象
let ret = Construct.call(obj, ...args)
// 处理返回值,引用类型,直接返回引用类型的值
if (ret !== null && /^(object|function)$/.test(typeof ret)) {
return ret
}
return obj
}
function Fn(name) {
this.name = name
this.age = function () {
console.log('方法===' + this.name)
}
}
let f1 = _new(Fn, '张三')
f1.age()
2.重构call
Function.prototype.myCall = function (ctx, ...params) {
// 参数可以为 undefined或null
ctx = ctx == null ? window : ctx
// 需要保证ctx必须是对象类型的值:因为只有对象才能设置属性
ctx = !/^(object|function)$/.test(typeof ctx) ? Object(ctx) : ctx
let self = this
let ret = null
// 新增的属性名保证唯一性,防止污染原始对象中的成员数据
let functionName = Symbol('functionName')
// 给对象添加属性
ctx[functionName] = self
// 执行方法
ret = ctx[functionName](...params)
// 删除自定义属性
delete ctx[functionName]
return ret
};
function fn(x, y) {
console.log(this, x, y)
}
let obj = {
name: '张三'
}
fn.myCall(obj, 2, 3)
3.重构apply
function fn1(a, b, c, ...d) {
console.log(this, a, b, c, d)
return '你的姓名为:' + this.name
};
var obj = { name: 2222, fn: 'abc' };
// 自定义一个apply方法,强制改变this指向
Function.prototype.myApply = function (Ctx, args) {
if (!Array.isArray(args)) throw new TypeError('参数2必须是数组');
// == 为null,判断是否为 null或undefined
// console.log(Ctx == null)
Ctx = Ctx == null ? window : (typeof Ctx != 'object' ? Object(Ctx) : Ctx)
// 给传入的对象添加一个属性
// 给自定义的属性添加一个随机名称
const methodName = Symbol();
// Ctx.fn = this
Ctx[methodName] = this
// 执行
// let ret = Ctx.fn()
let ret = Ctx[methodName](...args)
// 删除
delete Ctx[methodName]
// 返回
return ret;
}
4.重构bind
Function.prototype.myBind = function myBind(ctx, ...params) {
let self = this
return function (...args) {
self.apply(ctx, [...params, ...args])
}
};
var obj = { id: 1 }
function fn(...args) {
console.log(this, args)
}
btn.onclick = fn.bind(obj, 1, 2)
5.重构instanceof
es5的检测原理
检测构造函数的prototype是否出现在实例的__proto__上
es6的检测原理
构造函数 Symbol.hasInstance属性
function _instanceof(obj, FC) {
// 如果不是对象则抛异常
if (typeof obj !== 'object') throw new TypeError('必须是引用类型!');
// 对象不能是函数
if (typeof obj === "function") return false;
// obj为null 或 undefined 为 false
if (obj == null) return false;
// 得到当前FC原型
var prototype = FC.prototype;
if (!prototype) return false;
// 兼容性判断处理 环境支持es6 ==> typeof Symbol 暂时性死区,es6的判断
if (typeof Symbol === "function") {
// 返回true/false
var insFn = FC[Symbol.hasInstance];
if (typeof insFn === "function") { // 可以被调用执行
return insFn.call(FC, obj)
}
}
// 得到对象的原型 --- 针对于老版本浏览器,es5的判断
// var proto = obj.__proto__
var proto = Object.getPrototypeOf(obj)
//console.log(proto.__proto__) return false
while (1) {
if (proto === null) return false;
if (proto === prototype) return true;
// proto = proto.__proto__
proto = Object.getPrototypeOf(proto)
}
}
6.代理拦截
6.1defineProperty
ES5 提供了 Object.defineProperty 方法,该方法可以在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回这个对象。Object.defineProperty无法监听数组变化。
var obj = {
test:"hello"
}
//对象已有的属性添加特性描述
Object.defineProperty(obj,"test",{
// 能不能被配置(删除)
configurable:true | false,
// 是否可枚举
enumerable:true | false,
value:任意类型的值,
// 是否可写,此配置和 get/set不能同时存在
writable:true | false,
get(){},
set(value){}
});
// ===============================================
var data = {
name: '张三'
}
function observer(data) {
if (typeof data !== 'object') return data
for (let key in data) {
defineReactive(data, key, data[key])
}
}
function defineReactive(target, key, value) {
observer(value)
Object.defineProperty(target, key, {
get() {
return value
},
set(newVal) {
if (value !== newVal) {
value = newVal
update(key, value)
}
}
})
}
function update(key, value) {
document.querySelectorAll(`[v-bind="${key}"]`).forEach(el => {
if (el.tagName === 'INPUT') {
el.value = value
} else {
el.innerHTML = value
}
})
};
// 监听
observer(data);
// 初始化
document.querySelectorAll('[v-model]').forEach(el => {
el.addEventListener('keyup', function () {
let prop = this.getAttribute('v-model')
data[prop] = this.value
})
})
6.2 Proxy
Proxy是es6提供的新的api,Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。
(1)Proxy可以直接监听对象而非属性
(2)Proxy直接可以劫持整个对象,并返回一个新对象,不管是操作便利程度还是底层功能上都远强于Object.defineProperty。
(3)Proxy可以直接监听数组的变化
(4)Proxy有多达13种拦截方法,不限于apply、ownKeys、deleteProperty、has等等是Object.defineProperty不具备的。
const obj = { name: "张三" };
const proxy = new Proxy(obj, {
get(target, key) {
return target[key];
},
set(target, key, value) {
console.log(target, key, value)
target[key] = value;
return true;
}
});
proxy.age = 10;
console.log(proxy, obj);
字符串截取
const news = [
{
title: "海外网深一度:气候危机“逼近灾难临界值”,中国行动振奋世界"
},
{
title: "小区不准外卖员入内,杭州一外卖小哥回家被保安拦下!民警到场调解,小哥情绪崩溃"
},
{
title: "网传浙江慈溪上林中学一女生教室内被多次扇耳光 当地回应:已去处置"
}
];
const newsProxy = new Proxy(news, {
get(target, key) {
let title = target[key]['title'];
target[key]['title'] = title.length > 10 ? title.substr(0, 10) + '...' : title
return target[key]
}
});
console.log(newsProxy[0])
双向绑定
let obj = {
name: '张三',
}
const proxyHandler = {
get(target, key) {
if (typeof target[key] === "object") {
return new Proxy(target[key], proxyHandler)
}
return target[key]
},
set(target, key, value) {
let oldValue = target[key]
if (oldValue !== value) {
target[key] = value
update(key, value)
return true
}
}
}
let proxy = new Proxy(obj, proxyHandler)
function update(key, value) {
document.querySelectorAll(`[v-bind="${key}"]`).forEach(el => {
if (el.tagName === 'INPUT') {
el.value = value
} else {
el.innerHTML = value
}
})
}
// 初始化时
document.querySelectorAll('[v-model]').forEach(el => {
el.addEventListener('keyup', function () {
let prop = this.getAttribute('v-model')
proxy[prop] = this.value
})
})