JS核心原理 - 模块一 -继承进阶:如何实现new、apply、call、bind的底层逻辑

JS核心原理

模块一 基石篇

继承进阶:如何实现new、apply、call、bind的底层逻辑

思考问题:
	1、用什么样的思路可以new关键词?
	2、apply、call、bind这三个方法之间有什么区别?
	3、怎样实现一个apply或者call的方法?
new原理介绍
new关键词的主要作用:
	执行一个构造函数、返回一个实例对象
	根据构造函数的情况,来确定是否可以接受参数的传递
function Person() {
	this.name = 'jack';
}
var p = new Person();
console.log(p.name);		//jack
//new执行的操作:
//1、创建一个新对象
//2、将构造函数的作用域赋给新对象(this指向新对象)
//3、执行构造函数中的代码(为这个新对象添加属性)
//4、返回新对象
//无new的情况
function Person() {
	this.name = 'jack';
}
var p = Person();
console.log(p);			//undefined
console.log(name);		//jack
console.log(p.name)		//'name'of undefined

//有return的情况,return的是一个对象
function Person() {
	this.name = 'jack';
	return {age: 18}
}
var p = new Person();
console.log(p);			//{age: 18}
console.log(name);		//undefined
console.log(p.name)		//18

//有return的情况,return的不是一个对象
function Person() {
	this.name = 'jack';
	return 'tom'
}
var p = new Person();
console.log(p);			//{name: jack}
console.log(p.name)		//jack

new 关键词执行之后总是返回一个对象,要么是实例对象,要么是return语句指定的对象

apply、call、bind原理介绍
apply、call、bind是挂在Function对象上的三个方法
调用这三个方法的必须是一个函数
//基本语法
func.call(thisArg,param1,param2,...)
func.apply(thisArg,[param1,param2,...])
func.bind(thisArg,param1,param2,...)
//总结:
//共性:改变函数func的this指向
//call、apply的区别在于传参的写法不同,在改变this指向后立马执行
//bind和以上两个的区别是:不会马上执行
let a = {
	name: 'jack',
	getName: function() {
		return msg+this.name;
	}
}
let b = {
	name: 'lily'
}
console.log(a.getName('hello~'));		//hellp~jack
console.log(a.getName.call(b,'hi~'));	//hi~lily
console.log(a.getName.apply(b,'hi~'));	//hi~lily
let name = a.getName.bind(b,'hello~');	
console.log(name());					//hello~lily
apply、call、bind方法应用场景
下面几种应用场景的理念都是“借用”方法的思路
1、判断数据类型
	用Object.prototype.toString几乎可以判断所有类型的数据
function getType(obj) {
	let type = typeof obj;
	if(type !== "object"){
		return type;
	}
	return Object.prototype.toString.call(obj).replace(/^\[object(\s+)\]$/,'$1')
}
2、类数组的借用方法
	类数组因为不是真正的数组,所以没有数组类型上自带的种种方法,可以利用一些方法去借用数组的方法
// 借用数组的push方法
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}
3、获取数组的最大/最小值
	用apply来实现数组中判断最大/最小值,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
4、继承
function Parent3() {
	this.name = 'parent3';
	this.play = [1,2,3];
}
Parent3.prototype.getName = function() {
	return this.name;
}
function Child3() {
	//第二次调用Parent3()
	Parent3.call(this);
	this.type = 'child3';
}

//第一次调用Parent3()
Child3.prototype = new Parent3();
//手动挂上构造器,指向自己的构造函数
Child3.prototype.constructor = Child3;
var s3 = new Child3();
var s4 = new Child4();

s3.push(4);
console.log(s3.play,s4.play);		//不互相影响
console.log(s3.getName());			//正常输出'parent3'
console.log(s4.getName());			//正常输出'parent3' 
//问题:parent3执行了两次,多构造一次,就是一次性能开销
如何自己实现这些方法

手写实现new、call、apply、bind

1、new的实现
	new被调用后大致做了哪几件事情
	1、让实例可以访问私有属性
	2、让实例可以访问构造函数原型(constructor.prototype)所在原型链上的属性
	3、构造函数返回的最后结果是引用数据类型
function(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' && typeof res !==null;
	let isFunction = typeof res === 'function';
	return isObject || isFunction ? res:obj;
}
2、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) {
	var context = context || window;
	context.fn = this;
	var result = eval('context.fn(...args)');
	delete context.fn
	return result;	
}
//这两个方法是直接返回执行结果,而bind方法是返回一个函数,因此这里直接用eval执行得到结果
3、bind的实现
	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;
}
//返回的是一个函数

总结
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不甜的糖果

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值