房建设计图网站,华强北做电子网站建设,如东建设网站,企业简介如何写day-065-sixty-five-20230508-call()与bind()重写-JS中数据类型检测汇总-装箱与拆箱-类的多种继承方案
call()与bind()重写
call()重写 call()的作用例子 let obj {name: 珠峰培训
}
const fn function fn(x, y, ev) {console.log(this, x, y, ev)return x y
}
let res f…day-065-sixty-five-20230508-call()与bind()重写-JS中数据类型检测汇总-装箱与拆箱-类的多种继承方案
call()与bind()重写
call()重写 call()的作用例子 let obj {name: 珠峰培训
}
const fn function fn(x, y, ev) {console.log(this, x, y, ev)return x y
}
let res fn.call(obj, 10, 20)
console.log(res)自定义call()基本原理 let obj {name: 珠峰培训,
};
const fn function fn(x, y, ev) {console.log(this, x, y, ev);return x y;
};
// 需求把fn执行传递10、20并且让函数中的this指向obj
/* //不行的操作
// console.log(fn(10, 20)) //this:window x:10 y:20 -30
// console.log(obj.fn(10, 20)) //Uncaught TypeError: obj.fn is not a function obj此时和fn没有任何的关系obj.fn-undefined
*/
// call的基本原理
obj.fn fn
console.log(obj.fn(10, 20)) //手动让obj和fn有关系 自定义call()需求 let obj {name: 珠峰培训,
};
const fn function fn(x, y, ev) {console.log(this, x, y, ev);return x y;
};
// 需求把fn执行传递10、20并且让函数中的this指向obj
let res fn.call(obj, 10, 20);
console.log(res);
res fn.apply(obj, [10, 20]);
console.log(res);fn.call()的处理步骤 fn()这个函数首先基于__proto__找到Function.prototype.call()这个方法 把找到的call()方法执行 call()内部可以访问到的值 this:fncontext:objparams:[10,20] Function.prototype.call function call(context, ...params) {// this:fn「需要改变this指向的函数」// context:obj「需要改变的this指向」// params:[10,20] 「需要传递给fn函数的实参」context.AAA this; // obj.AAA fnlet result context.AAA(...params); // obj.AAA(10,20)delete context.AAA; // delete obj.AAAreturn result;
};而在call()方法内部把fn()执行把它里面的this改为obj并且为其传递了10与20这些入参接收它的返回值并作为call函数执行的返回值 简洁初步版 Function.prototype.call function call(context, ...params) {// this:fn「需要改变this指向的函数」// context:obj「需要改变的this指向」// params:[10,20] 「需要传递给fn函数的实参」context.AAA this; // obj.AAA fnlet result context.AAA(...params); // obj.AAA(10,20)delete context.AAA; // delete obj.AAAreturn result;
};初步完善版 Function.prototype.call function call(context, ...params) {//如果context传递的是null/undefined则让函数中的this指向window对象if (context null){ context window;} //如果传递的context值是原始值类型是无法为其设置成员的此时我们需要把其转换为对象类型「非标准特殊对象」if (!/^(object|function)$/.test(typeof context)){ context Object(context);} let result,key Symbol(KEY); //保证给对象新增的成员不会和对象以往的成员有冲突context[key] this;result context[key](...params);delete context[key]; //临时设置的这个成员仅仅是为了把函数执行可以改变其内部的this用完后需要移除return result;
};bind()重写 需求 let obj {name: 珠峰培训,
};
const fn function fn(x, y, ev) {console.log(this, x, y, ev);return x y;
};
// 需求点击BODY执行fn让fn中的this指向obj传递给fn函数10/20包括事件对象也作为最后一个参数传递给fn
/* //不行的操作
// document.body.onclick fn //点BODY才会执行fn - this:document.body x:事件对象 y:undefined
// document.body.onclick fn.call(obj, 10, 20) //还不等到点击就把fn执行了虽然this和参数都改了但是ev事件对象没有它相当于先把fn执行把执行的结果“30”赋值给点击事件...
*/bind基本原理 let obj {name: 珠峰培训,
};
const fn function fn(x, y, ev) {console.log(this, x, y, ev);return x y;
};
// 需求点击BODY执行fn让fn中的this指向obj传递给fn函数10/20包括事件对象也作为最后一个参数传递给fn
document.body.onclick function (ev) {// this:document.body ev:事件对象// 在此函数执行的时候再把我们真正需要执行的函数fn执行return fn.call(obj, 10, 20, ev);
};用bind解决 let obj {name: 珠峰培训,
};
const fn function fn(x, y, ev) {console.log(this, x, y, ev);return x y;
};
// 需求点击BODY执行fn让fn中的this指向obj传递给fn函数10/20包括事件对象也作为最后一个参数传递给fn
document.body.onclick fn.bind(obj, 10, 20);
/*
document.body.onclick function anonymous(ev) { ... }
//当点击的时候先把bind返回的匿名函数执行
*/简洁初步版 Function.prototype.bind function bind(context, ...params) {// this:fn context:obj params:[10,20]let self thisreturn function anonymous(ev) {// this:document.body ev:事件对象// 目的把fn执行改变其this指向并且传递一个个的实参params.push(ev)return self.call(context, ...params)}
}使用例子 let obj {name: 珠峰培训,
};
const fn function fn(x, y, ev) {console.log(this, x, y, ev);return x y;
};Function.prototype.bind function bind(context, ...params) {// this:fn context:obj params:[10,20]let self this;return function anonymous(ev) {// this:document.body ev:事件对象// 目的把fn执行改变其this指向并且传递一个个的实参params.push(ev);return self.call(context, ...params);};
};
document.body.onclick fn.bind(obj, 10, 20);初步完善版 Function.prototype.bind function bind(context, ...params) {let self thisreturn function anonymous(...args) {return self.call(context, ...params.concat(args))}
}使用例子 let obj {name: 珠峰培训,
};
const fn function fn(x, y, ev) {console.log(this, x, y, ev);return x y;
};Function.prototype.bind function bind(context, ...params) {let self this;return function anonymous(...args) {return self.call(context, ...params.concat(args));};
};
document.body.onclick fn.bind(obj, 10, 20);for-if版-最简基础版 Function.prototype.bind function bind(context, ...params) {let self thisreturn function anonymous(...args) {return self.call(context, ...params,...args)}
}const bind function bind(callback, context, ...params) {let self callback;return function anonymous(...args) {//return self.call(context, ...params,...args)//连call()也不用// 自己手动实现call()用原生的可以替代但为了最基础原理直接集合在这。if (/^(object|function)$/.test(typeof context)) {context window; //undefined//严格模式下}if (typeof context ! object) {context Object(context);}let key Symbol(call-key);let hasProto false;let theContextProto Object.getPrototypeOf(context);if (typeof theContextProto object) {hasProto true;} else {Object.setPrototypeOf(context,{});}theContextProto[key] self;let res context[key](...params, ...args);if (hasProto) {delete theContextProto[key];} else {Object.setPrototypeOf(context,null);}return res;};
};Function.prototype.bind function (context, ...params) {bind(this, context, ...params);
};JS中数据类型检测汇总
JS中数据类型检测汇总 typeof [value] 返回值首先是一个字符串其次字符串中包含了对应的数据类型 number object function … 所以两个及两个以上的typeof所得的值必定为字符串string console.log(typeof typeof typeof [10, 20]) //string特点 typeof null //-- object不能细分对象基于typeof检测对象的时候除了函数会返回function其余的都返回object。基于typrof检测一个未被声明的变量不会报错结果是undefined。 不过暂时性死区除外依旧会报错。 原理 typeof在检测数据类型的时候是按照数据值在计算机底层存储的二进制值进行检测的 其中如果二进制值的前三位是零便都被识别为对象。 然后再看此对象是否具备call方法 具备call方法则返回function说明其是一个函数不具备的返回object说明其是其它对象 而null在计算机底层的二进制值都是零也就是前三位也是零所以 typeof null 的结果是 “object” 应用 检测除null之外的原始值类型 typeof用起来方便而且性能还好 判断兼容性 查看如Promise是否存在 笼统检测是否为对象… 对象 instanceof 构造函数 原本的意义是用来检测某个对象是否是相应类的实例 只不过针对于这个特点可以用其检测一些数据类型 检测是否为数组值 instanceof Array检测是否为正则值 instanceof RegExp //RegExp是Regular Expressions的简写… 也就是基于instanceof可以弥补typeof不能细分对象的缺陷! 特点 无法检测原始值类型返回结果都是false 原因基于instanceof进行操作不会对原始值类型进行装箱 10 instanceof Number - false 原本不是检测数据类型的现在非要让其检测类型所以检测的结果不一定精准 const Fn function Fn() {this.x 10;
};
Fn.prototype new Array();
Fn.prototype.name Fn;
let f new Fn();
// f.__proto__ - Fn.prototype - Array.prototype - Object.prototype
console.log(f instanceof Fn); //true
console.log(f instanceof Object); //true
console.log(f instanceof Array); //true//虽然也因为原型链的关系值是true但f连length这个属性都没有强行使用Array的方法可能会报错。
console.log(f instanceof RegExp); //false
console.log(f);原理 首先看 构造函数Ctor 是否具备Symbol.hasInstance这个属性 具备CtorSymbol.hasInstance不具备依次查找对象的原型链__proto__,一直到Object.prototype在此过程中如果构造函数prototype出现在了其原型链的某个环节则说明当前对象是此构造函数的一个实例检测结果就是true! 直接Symbol.hasInstance得用ES6中class类写法中的static关键字并在class中声明 class Fn {constructor(name) {if (name) this.name name;}// 设置其静态私有属性方法「这样重写会生效」static [Symbol.hasInstance](obj) {//这里可以针对一些特定属性进行检测进而让符合条件的才表示是该构造函数类的实例。return obj.hasOwnProperty(name);}
}
let f1 new Fn();
let f2 new Fn(珠峰);
console.log(f1 instanceof Fn); //false Fn[Symbol.hasInstance](f1)
console.log(f2 instanceof Fn); //true Fn[Symbol.hasInstance](f2)直接用ES5的方法设置是不行的。 // 正常情况下直接这样重写 Symbol.hasInstance 是不会生效的
Array[Symbol.hasInstance] function (obj) {console.log(AAA);
};
const Fn function Fn() {};
Fn.prototype new Array();
Fn.prototype.constructor Fn;
let f new Fn();
console.log(f);
console.log(f instanceof Array); //true 等价于 Array[Symbol.hasInstance](f)在支持ES6的浏览器中Function.prototype上具备Symbol.hasInstance方法所以只要是函数也都具备这个方法! arr instanceof Array //-- Array[Symbol.hasInstance](arr)/*
_instanceof对内置 instanceof 运算符的重写paramsobj要检测的对象Ctor要被检测的构造函数return布尔值
*/
const _instanceof function _instanceof(obj, Ctor) {// 必须保证Ctor得是一个函数if (typeof Ctor ! function) {throw new TypeError(Right-hand side of instanceof is not callable);}// instanceof无法检测原始值类型if (obj null || !/^(object|function)$/.test(typeof obj)) {return false;}// Ctor这个函数必须具备prototypeif (!Ctor.prototype) {throw new TypeError(Function has non-object prototype undefined in instanceof check);}if (Ctor[Symbol.hasInstance]) {// 具备 Symbol.hasInstance 的构造函数则直接基于这个方法执行去进行校验即可return Ctor[Symbol.hasInstance](obj);}let proto Object.getPrototypeOf(obj); //获取对象的原型链类似于 obj.__proto__// 一直找直到找到尽头为止while (proto) {if (proto Ctor.prototype) return true; //在查找中如果出现了Ctor.prototype说明对象是此构造函数的一个实例返回true即可proto Object.getPrototypeOf(proto); // 获取其原型对象的原型链}return false; //都找完也没有符合条件的说明对象不是此构造函数的一个实例直接返回false
};
let arr [10, 20];
console.log(_instanceof(arr, Array)); //true
console.log(_instanceof(arr, Object)); //true
console.log(_instanceof(arr, RegExp)); //falseconstructor 获取对象的构造函数从而判断是否是属于某一个数据类型。 let arr [10, 20];
console.log(arr.constructor Array); //true
console.log(arr.constructor Object); //false 如果一个对象的constructor等于Object说明该对象的__proto__直接指向Object.prototype也就是说明此对象是“标准普通对象”
console.log(arr instanceof Array); //true
console.log(arr instanceof Object); //true可以检测基本数据类型。 let num 10;
console.log(num.constructor Number); //true 支持原始值类型的检测因为访问 constructor 属性的时候原始值会默认的进行装箱只不过这种方式我们一般很少去使用。 因为constructor值是可以被更改的。 修改值的成本低一但被更改则检测结果是不准确的比如可以设置一个私有属性叫constructor。 不过这个可以让其与实例对象的原型上的constructor进行比较进而让修改对象的成本变高也更准确。 Object.prototype.toString.call([value]) 不仅仅Object.prototype上有toString方法在Number()/String()/Boolean()/Array()/Function()…等的原型对象上也有toString方法。 只不过其它原型上的toString方法都是用来转换为字符串的只有Object.prototype.toString是用来检测数据类型的。 let arr [10, 20];
console.log(arr.toString()); //10,20 使用的是Array.prototype.toString
let obj { x: 10, y: 20 };
console.log(obj.toString()); //[object Object] 使用的是Object.prototype.toString把Object.prototype上的toString方法执行让方法中的this指向要检测的数据值这样就可以返回此数据值的数据类型 - [object ?] const toString Object.prototype.toString;
let arr [10, 20],obj { x: 10, y: 20 };
console.log(toString.call(arr)); //[object Array]
console.log(toString.call(obj)); //[object Object]更简洁地使用 let toString Object.prototype.toString.call.bind(Object.prototype.toString)//不用call()来改指向this的指向了。
toString(String())//[object Null]特点 精准且强大 唯一不足就是写起来麻烦一点相比于typeof。 [object ?]中的?在一般情况下就是检测值所属的构造函数。 例子 const toString Object.prototype.toString
toString.call(null) //- “[object Null]” 虽然Null不是构造函数但是结果还是很准的
toString.call(undefined) //- “[object Undefined]”
toString.call([]) //- “[object Array]”
toString.call(/\d/) //- “[object RegExp]”
//...前提是内置的构造函数。 function Fn() {}
let f new Fn();
console.log(toString.call(f)); //[object Object]如果被检测的值具备Symbol.toStringTag这个属性那么属性值是什么最后检测结果[object ?]中的?就是什么。 这个一般是自定义构造函数中来使用的以便更像原始对象类型可以让Object.toString()兼容。 const toString Object.prototype.toString;
function Fn() {}
Fn.prototype[Symbol.toStringTag] Fn;
let f new Fn();
console.log(toString.call(f)); //[object Fn]在Promise.prototype上具备Symbol.toStringTag这个属性属性值是Promise。 let p new Promise((){})
toString.call(p)// - [object Promise]此办法虽然不错但是也不是所有的数据类型检测都使用这个办法。 一般来说 需要笼统地检测或者按照大的类别去检测使用typeof会更方便。而需要很精准地检测的时候使用toString会更好! 快捷方法 isNaN 检测是否为有效数字Array.isArray 检测是否为数组… 对象对象的笼统定义说明 身上有键值对或理论上可以定义及设置、在保存之后依旧可以访问到的设置的键值对属性值的变量。
各种检测方法 检测JavaScript内置的基础对象或包装对象的类型。 // 检测JavaScript内置的基础对象或包装对象的类型。
const toString Object.prototype.toString;检测是否为数组。 // 检测是否为数组。
const isArray Array.isArray;把函数转为字符串。 // 把函数转为字符串。
const fnToString Function.prototype.toString;万能检测数据类型的方法; // 检测JavaScript内置的基础对象或包装对象的类型。
const toString Object.prototype.toString;
// 检测是否为数组。
const typeReg /^(object|function)$/;// 万能检测数据类型的方法;
//返回小写的入参的值类型字符串
const isType function isType(obj) {if (obj null || obj undefined) {return obj ;}let type typeof obj;let reg /^\[object (\w)\]$/;let res type;if (typeReg.test(type)) {res reg.exec(toString.call(obj))[1].toLowerCase(); //对象类型;}return res;
};检测是否为对象。 // 检测是否为对象
const isObject function isObject(obj) {return obj ! null typeReg.test(typeof obj);
};检测是否是window对象。 // 检测是否是window对象
const isWindow function isWindow(obj) {return obj ! null obj obj.window;
};检测是否为函数。 // 检测是否为函数
const isFunction function isFunction(obj) {return typeof obj function;
};检测是否为数组或者伪数组。 // 检测是否为数组。
const isArray Array.isArray;
// 检测是否是window对象
const isWindow function isWindow(obj) {return obj ! null obj obj.window;
};// 检测是否为函数
const isFunction function isFunction(obj) {return typeof obj function;
};
// 检测是否为数组或者伪数组
const isArrayLike function isArrayLike(obj) {if (isArray(obj)) return true;let length !!obj length in obj obj.length;if (isFunction(obj) || isWindow(obj)) return false;return (length 0 ||(typeof length number length 0 length - 1 in obj));
};检测是否为一个纯粹的对象(标准普通对象)。 // 检测JavaScript内置的基础对象或包装对象的类型。
const toString Object.prototype.toString;
// 检测是否为数组。
const typeReg /^(object|function)$/;// 把函数转为字符串。
const fnToString Function.prototype.toString;// 检测是否为函数
const isFunction function isFunction(obj) {return typeof obj function;
};
// 万能检测数据类型的方法;
//返回小写的入参的值类型字符串
const isType function isType(obj) {if (obj null || obj undefined) {return obj ;}let type typeof obj;let reg /^\[object (\w)\]$/;let res type;if (typeReg.test(type)) {res reg.exec(toString.call(obj))[1].toLowerCase(); //对象类型;}return res;
};
// 检测是否为一个纯粹的对象(标准普通对象)
const isPlainObject function isPlainObject(obj) {if (isType(obj) ! object) {return false;}let proto, Ctor;proto Object.getPrototypeOf(obj);if (!proto) {return true; //匹配 Object.create(null) 这种情况}Ctor proto.hasOwnProperty(constructor) proto.constructor;return (isFunction(Ctor) fnToString.call(Ctor) fnToString.call(Object));
};检测是否为空对象。 // 检测是否为对象
const isObject function isObject(obj) {return obj ! null typeReg.test(typeof obj);
};
// 检测是否为空对象
const isEmptyObject function isEmptyObject(obj) {if (!isObject(obj)) {throw new TypeError(obj is not an object);}let keys Reflect.ownKeys(obj);return keys.length 0;
};检测是否为有效数字。 // 检测JavaScript内置的基础对象或包装对象的类型。
const toString Object.prototype.toString;
// 检测是否为数组。
const typeReg /^(object|function)$/;// 万能检测数据类型的方法;
//返回小写的入参的值类型字符串
const isType function isType(obj) {if (obj null || obj undefined) {return obj ;}let type typeof obj;let reg /^\[object (\w)\]$/;let res type;if (typeReg.test(type)) {res reg.exec(toString.call(obj))[1].toLowerCase(); //对象类型;}return res;
};
// 检测是否为有效数字
const isNumeric function isNumeric(obj) {let type isType(obj);return (type number || type string) !isNaN(obj);
};装箱与拆箱
let num 10 //原始值类型
console.log(num.toFixed(2)) //10.00 默认进行了“装箱”操作把原始值转换为对象num-Object(num)
let num2 new Number(10) //对象类型
console.log(num2 10) //20 默认进行了“拆箱”操作把对象转换为原始值num2[Symbol.toPrimitive] - num2[valueOf]() 把原始值变成对象类型就是装箱操作 比如对原始值变量调用包装类的方法(10).toFixed(2)//10.00 把对象类型变成原始值就是拆箱操作 比如数学运算Number(10) 10//20
类的多种继承方案
类的多种继承方案
多态 多态一个函数的多种形态 相同的方法名但是因为 参数个数、类型、返回值类型 等不同构建出了多个不同作用的同名方法。 是因为后端很多请求都调用同一个方法的话会导致某个方法的压力比较大 前端JS中是没有类似于后台这样的多态处理的 因为前端大多是客户端由客户来操作客户一个人的压力而已。前端多态同一个函数根据传参不同在函数中做不同的事情 压力可以用模块来分担。 后端多态 public void sum(int x,int y){//.....
}
public String sum(int x,int y,boolean flag){//.....
}
sum(10,20)
sum(10,20,true)前端多态 function sum(x,y,flag){//....
}
//前端多态同一个函数根据传参不同我们在函数中做不同的事情继承
继承的目的让子类的实例除了具备子类提供的属性方法还要具备父类提供的属性和方法。实现继承的方案 原型继承 让子类的原型对象指向父类的一个实例。 Child.prototype new Parent()
//子类构造函数.prototype父类构造函数问题 父类提供的属性和方法全部都成为子类实例公有属性和方法。 一般想要的是父类公有的变成子类公有的。父类私有的变成子类私有的。 子类的原型对象被重定向后丢失了constructor属性。 可以手动加上用以弥补。 Child.prototype.constructorChild
//子类构造函数.prototype.constructor子类构造函数特点 原型继承并不是拷贝式继承也就是并不是把父亲的东西copy一份给儿子而是让儿子基于原型链找到父亲的方法再去使用。 拷贝式继承就是把父亲的东西copy一份给儿子之后就是儿子的东西了。 儿子怎么改动这些属性与方法都与父亲关系不大。 原型继承是非拷贝式继承这样就存在一个隐患 如果儿子基于原型链把父亲原型上的方法改了这样对父亲的其它实例也有影响。 原则上子类修改了自己能修改的属性或方法后一定不会影响到父类或其它实例。 // 父类
function Parent() {this.x 10;this.y 20;
}
Parent.prototype.sum function sum() {return this.x this.y;
};// 子类
function Child() {this.x 100;this.z 300;
}
Child.prototypenew Parent()
Child.prototype.constructorChild
Child.prototype.minus function minus() {return this.z - this.x;
};let cnew Child()
console.log(c--, c);call继承 把父类当做普通函数执行让函数中的this指向子类的实例问题 仅仅实现了把父类私有的属性继承给了子类实例的私有属性 有冲突则以子类为主但是父类公有的方法并没有被子类实例继承过去 特点 它是拷贝式继承。 // 父类
function Parent() {this.x 10;this.y 20;
}
Parent.prototype.sum function sum() {return this.x this.y;
};// 子类
function Child() {Parent.call(this)//this:子类的实例cthis.x 100;this.z 300;
}Child.prototype.minus function minus() {return this.z - this.x;
};let cnew Child()
console.log(c--, c);寄生组合式继承 把call继承和变异版原型继承混合在一起基于这样的方案就实现了父亲私有的给了儿子私有的父亲公有的给了儿子公有 // 父类
function Parent() {this.x 10;this.y 20;
}
Parent.prototype.sum function sum() {return this.x this.y;
};// 子类
function Child() {Parent.call(this)//call继承部分拷贝式继承this.x 100;this.z 300;
}
//原型继承部分非拷贝式继承
Child.prototype Object.create(Parent.prototype)
Child.prototype.constructorChild
Child.prototype.minus function minus() {return this.z - this.x;
};let cnew Child()
console.log(c--, c);基于ES6中的class创建类其自带了继承方案 原理类似于寄生组合式继承
进阶参考