这是我参与8月更文挑战的第N天,活动详情查看: 8月更文挑战”juejin.cn/post/698796…
new基本原理
主要作用
执行一个构造函数,返回一个新的实例对象。
在执行过程中,根据构造函数的参数情况来确定是否需要参数的传递。
执行步骤
- 创建一个新对象
- 将构造函数的作用域指赋给新对象
- 执行构造函数中代码
- 返回新对象
var ObjectFactory = function () {
// 创建一个对象
var obj = new Object()
// 返回第一个参数作为构造函数
var Constructor = [].shift.call(arguments)
// 将构造函数的原型复制于对象的原型
obj.__proto__ = Constructor.prototype
// 调用构造函数,并将obj 作为this, arguments作为参数
var ret = Constructor.apply(obj, arguments)
// 如果构造函数里返回一个对象的话,就直接返回,否则我们就返回this即new创建的对象
return typeof ret === 'object'? ret: obj
}
// 效果等效
var a = ObjectFactory(Person, 'sven');
复制代码
注意点
-
不使用new且构造函数没有返回值
- 相当于正常函数执行,此时函数中this指向window
-
构造函数中有返回值
-
返回一个对象
- 因为new要求必须返回一个对象,所以如果返回的是一个与this无关的对象,则最终的值就是该对象而不是new生成的this对象
function Person(){ this.name = 'Jack'; return {age: 18} } var p = new Person(); console.log(p) // {age: 18} console.log(p.name) // undefined console.log(p.age) // 18 复制代码
-
-
返回的不是对象
- 还会按照 new 的实现步骤,返回新生成的this对象
new 关键词执行之后总是会返回一个对象,要么是实例对象,要么是 return 语句指定的对象。
apply、call、bind基本原理
基本情况
call、apply 和 bind 是挂在 Function 对象上的三个方法,调用这三个方法的必须是一个函数。
基本语法
func.call(thisArg, param1, param2, ...)
func.apply(thisArg, [param1,param2,...])
func.bind(thisArg, param1, param2, ...)
复制代码
区别
-
相同点:都可以改变函数 func 的 this 指向。
-
不同点:
-
call 和 apply 的区别在于,传参的写法不同:
- apply 的第 2 个参数为数组;
- call 则是从第 2 个至第 N 个都是给 func 的传参;
-
bind 和这两个(call、apply)又不同,
- bind 虽然改变了 func 的 this 指向,但不是马上执行
- 这两个(call、apply)是在改变了函数的 this 指向之后立马执行。
-
使用场景
判断数据类型
function getType(obj){
let type = typeof obj;
if(type !== "object"){
return type;
}
return Object.prototype.toString.call(obj).replace(/^[Object (\S+)]$/,'$1');
}
复制代码
数据通过 call 借用了 toString 方法来达到数据类型的判断
类数组借用数组方法
类数组因为不是真正的数组,所以无法使用数组的方法,只能通过call借用
var arrayLike = {
0: 'java',
1: 'script',
length: 2
}
Array.prototype.push.call(arrayLike, 'jack', 'lily');
console.log(typeof arrayLike); // 'object'
console.log(arrayLike);
// {0: "java", 1: "script", 2: "jack", 3: "lily", length: 4}
复制代码
获取最大值、最小值
用apply来实现,可以直接传递数组为参数,又可以避免进一步展开数组。
let arr = [13, 6, 10, 11, 16];
const max = Math.max.apply(Math, arr);
const min = Math.min.apply(Math, arr);
console.log(max); // 16
console.log(min); // 6
复制代码
继承
function Parent1(){
this.name = 'parent1';
}
Parent1.prototype.getName = function () {
return this.name;
}
// 构造函数式继承
function Child1(){
Parent1.call(this); // Parent1.apply(this) Parent1.bind(this)()
this.type = 'child1'
}
// 组合式继承
Child1.prototype = new Parent1();
Child1.prototype.constructor = Child1;
let child = new Child1();
console.log(child); // 没问题 {name: "parent1", type: "child1"}
console.log(child.getName()); // 会报错
复制代码
方法实现
new方法
function _new(ctor, ...args) {
if(typeof ctor !== 'function') {
throw 'ctor must be a function';
}
let obj = new Object();
obj.__proto__ = Object.create(ctor.prototype);
let res = ctor.apply(obj, [...args]);
let isObject = typeof res === 'object' && res !== null;
let isFunction = typeof res === 'function';
return isObject || isFunction ? res : obj;
};
复制代码
apply和call方法
两个方法基本原理是差不多的,只是参数存在区别
Function.prototype.call = function (context, ...args) {
var context = context || window;
context.fn = this;
var result = eval('context.fn(...args)');
delete context.fn
return result;
}
Function.prototype.apply = function (context, args) {
let context = context || window;
context.fn = this;
let result = eval('context.fn(...args)');
delete context.fn
return result;
}
复制代码
bind方法
bind 的实现思路基本和 apply 一样,但是在最后实现返回结果这里,bind 和 apply 有着比较大的差异,bind 不需要直接执行,因此不再需要用 eval ,而是需要通过返回一个函数的方式将结果返回,之后再通过执行这个结果,得到想要的执行效果。
Function.prototype.bind = function (context, ...args) {
if (typeof this !== "function") {
throw new Error("this must be a function");
}
var self = this;
var fbound = function () {
self.apply(this instanceof self ? this : context, args.concat(Array.prototype.slice.call(arguments)));
}
if(this.prototype) {
fbound.prototype = Object.create(this.prototype);
}
return fbound;
}
复制代码
实现 bind 的核心在于返回的时候需要返回一个函数,故这里的 fbound 需要返回,
但是在返回的过程中原型链对象上的属性不能丢失。因此这里需要用Object.create 方法,将 this.prototype 上面的属性挂到 fbound 的原型上面,最后再返回 fbound。
这样调用 bind 方法接收到函数的对象,再通过执行接收的函数,即可得到想要的结果