new 原理介绍
new 关键词的主要作用是执行一个构造函数、返回一个实例对象,根据构造函数的怀况中,来确定是否可以接受参数的传递。
function Person() {
this.name = 'Jack'
}
let p = new Person()
console.log(p.name) // Jack
new 在生成实例的过程中进行的步骤:
- 创建一个新对象
- 将构造函数的作用域赋给新对象(this指向新对象)
- 执行构造函数中的代码(为这个新对象添加属性)
- 返回新对象
当构造函数中有return一个对象的操作时:
function Person() {
this.name = 'Jack'
return{age:18}
}
let p = new Person()
console.log(p) // {age:18}
console.log(p.name) // undefined
console.log(p.age) // 18
上面代码可以看出,当构造函数return一个和this无关的对象时,new会直接返回这个新对象 ,而不是通过new执行步骤生成的this对象 。
注:构造函数必须返回是一个对象,如果返回不是对象,那么还是按照new的实现步骤返回新生成的对象。
function Person() {
this.name = 'Jack'
return 'tom'
}
let p = new Person()
console.log(p) // {name:'Jack'}
console.log(p.name) // Jack
new 被调用后大致做了哪几件事情
- 让实例可以访问到私有属性
- 让实例可以访问构造函数原型(constructor.prototype)所在原型链上的属性
- 构造函数返回的最后结果是引用数据类型
总结:new关键词执行之后总是会返回一个对象,要么是实例对象,要么是return语句指定的对象
apply & call & bind 原理介绍
call 、apply 和 bind 是挂在 Function 对象上的三个方法,调用这三个方法必须是一个函数
语法:
func.call(thisArg,param1,param2,...)
func.apply(thisArg,param1,param2,...)
func.bind(thisArg,param1,param2,...)
thisArg为this所指向的对象,后面的param1,param2为函数的function的多个参数,如果不需要参数可不写
相同点:都可改变 function 的 this 指向
call & apply 区别:
传参的写法不同
- apply第二个参数为数组
- call则是第二个至第N个都是给fcuntion的传参
bind 与 call & apply区别:
- bind虽然改变了function的this指向,但不是马上执行
- 而call & apply 是在改变function的this指向后立即执行
借用
A对象有个getName的方法,B 对象也需要临时使用同样的方法,那么这个时候可以借用A 对象的getName方法
例:
let a = {
name: 'jack',
getName: function(msg) {
return msg + this.name
}
}
let b = {
name: 'lily'
}
console.log(a.getName('hello~')) // hello~jack
console.log(a.getName.call(b, 'hello~')) // hello~lily
console.log(a.getName.apply(b, ['hello~'])) // hello~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(/^$/, '$1')
}
判断数据类型就是借用了Object.prototype.toString方法,最后返回用来判断传入的object字符串来确定最后的数据类型
2、类数组的借用方法
类数组因为不是真正的数组,所以没有数组类型上自带的种种方法,可以利用一些方法云借用数组的方法。
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
继承
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'
}
Child3.prototype = new Parent3()
// 手动挂上构造器,指向自己的构造函数
Child3.prototype.constructor = Child3
var s3 = new Child3()
s3.play.push(4)
console.log(s3.getName()) //parent3
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 方法是返回一个函数,因此这里直接用 eval 执行得到结果
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
}