总述
call(); apply(); bind(); 这三个方法是定义在Function.prototype中的.
因为js中的所有函数,都可以看成是对象, 都是由Function构造函数实例化出来的.
console.dir(Function.prototype)
call()
我们先看下面代码,并思考一个问题,如何改变test方法的this指向呢?从而让其可以打印出‘张三’
let obj1 = {
name: '张三'
}
let obj2 = {
name: '李四',
test: function () {
console.log(this.name)
}
}
obj2.test()//分别打印:'李四'、'老六'
call()语法: 方法.call(newThis,a1,a2……)
newThis,就是你要指向的新的对象,后面则是正常的参数
let obj1 = {
name: '张三'
}
let obj2 = {
name: '李四',
test: function (a) {
console.log(this.name)
console.log(a)
}
}
// obj2.test('老六')//分别打印:'李四'、'老六'
obj2.test.call(obj1, '老八') //37行打印——'张三',38行打印——'老八'
利用call(),将一个伪数组转换为真数组
let obj = {
0: '张三',
1: '李四',
2: '王五',
3: '赵六',
4: '冯七',
length: 5
}
//将数组原型对象的方法--slice 的this指向改为obj,截取从下标0开始到最后的元素,返回一个真数组:arr
let arr = Array.prototype.slice.call(obj, 0)
console.log(arr)
结果:
万能数据类型检测
//Object.prototype.toString() 会返回一个 "[object type]",其中 type 是被检测数据的类型
console.log(Object.prototype.toString.call(null))
console.log(Object.prototype.toString.call(undefined))
console.log(Object.prototype.toString.call(12))
console.log(Object.prototype.toString.call('床前明月光'))
console.log(Object.prototype.toString.call([]))
console.log(Object.prototype.toString.call({}))
console.log(Object.prototype.toString.call(Symbol()))
console.log(Object.prototype.toString.call(true))
console.log(Object.prototype.toString.call(() => {}))
apply()
语法: 函数名.apply(this的新指向,数组或者伪数组);
function sum (num1, num2) {
console.log(this)
console.log(num1 + num2)
}
let obj = {
name: '刀客'
}
sum.apply(obj, [1, 10])
此时打印结果如下:
利用apply(),将一个伪数组转换为真数组
let obj = {
0: '张三',
1: '李四',
2: '王五',
3: '赵六',
4: '冯七',
length: 5
}
let arr = []
arr.push.apply(arr, obj)
console.log(arr)
求数组最大值
let arr = [1, 2, 3, 4, 5, 6, 7, 8]
//老方法如下
// let num = -Infinity
// let arr2 = []
// for (key in arr) {
// if (arr[key] > num) {
// num = arr[key]
// }
// }
let max = Math.max.apply(Math, arr)
console.log(max) // 8
bind()
语法: 函数名.bind(this的新指向,可选参数);
let obj1 = {
name: '张三'
}
let obj2 = {
name: '李四',
test: function (a, b) {
console.log(this.name)
console.log(a + ',' + b)
}
}
let func = obj2.test.bind(obj1, '行到水穷处', '坐看云起时')
// 上面的和下面的都可以传参,上面的优先级高
func()
修改定时器中函数里的this指向
// bind()不会立即执行函数,而是会返回一个改了this指向,但是函数体是一样的这么一个函数回来.
let obj1 = {
name: '张三'
}
setTimeout(
function fn () {
console.log(this)
console.log('定时器')
}.bind(obj1),
1000
)
如上所示,将this
由window
转变为了obj1
三个方法的异同
call()特点
- 会立即执行函数
- 可传入多个参数
- 可改变
this
指向
apply() 特点
- 会立即执行函数
- apply只有2个参数,第一个是this的新指向,第二个是数组或者伪数组.
- 可改变
this
指向
bind() 特点
- 不会立即执行函数,而是会返回一个改了this指向,但是函数体是一样的这么一个函数回来.
- apply第2个参数可选
- 可改变
this
指向
后述
若使用函数上下文调用模式修改this,this的新指向不是一个对象,而是一个普通类型,那this指向谁?
function test () {
console.log(this)
}
function fn () {}
let arr = []
let obj = {}
let nul = null
test.call(123) //this指向基本包装类型Number对象
test.call(NaN) //this指向基本包装类型Number对象
test.call('abc') //this指向基本包装类型String对象
test.call(true) //this指向基本包装类型Boolean对象
test.call(null) //this指向window对象
test.call(undefined) //this指向window对象
console.log(typeof fn)
console.log(typeof arr)
console.log(typeof obj)
console.log(typeof nul)
console.log(arr.__proto__.constructor)
console.log(obj.__proto__.constructor)
console.log(obj.__proto__.__proto__ == nul)
console.log(nul instanceof Array)
console.log(obj instanceof Object)
打印结果如下图:
后记——继承
//父亲构造函数(姓氏,汽车)
function Father (xing, car) {
this.xing = xing
this.car = car
}
//儿子构造函数
function Son (xing, car, job) {
// this.xing = xing;
// this.car = car;
// 上面这两句话,在Father构造函数里已经写过了.那我这里就不想重复的去写.
// 就想继承
// Father(xing,car); //window.Father(xing,car)
// 上面这句话不行,因为这样的话执行Father函数,函数体的this是window..那就相当于是给window对象加了xing和car
// 而我们现在希望调用Father的时候,他里面this是new关键字创建出来的对象
// 方法1
// Person.call(this,name,age)
// 方法2
// Person.apply(this,[name,age])
// 方法3
// Father.bind(this) 会返回一个新的函数,所以,可以在调用是传入参数
Father.bind(this)(xing, car)
this.job = job
}
//实例化学生对象
let s1 = new Son('欧阳', '五菱', '码农')
console.log(s1)
组合继承
//人构造函数
function Person (name, age) {
this.name = name
this.age = age
}
//人构造函数对应的原型
Person.prototype.sayHi = function () {
console.log('你好,我的名字是' + this.name)
}
//---------------------------------------------
//学生构造函数
function Student (name, age, score) {
//借用构造函数继承
Person.call(this, name, age)
this.score = score
}
//每一个学生也有打招呼的方法.可以替换原型继承
Student.prototype = new Person()
//每一个学生也有学习的方法.往原型中添加学习的方法
Student.prototype.study = function () {
console.log(`我是学生,我的名字是${this.name}我在学习`)
}
//实例化学生对象
let s1 = new Student('张三', 18, 100)
console.log(s1)
s1.sayHi()
s1.study()
问题
上面的代码中:
Student.prototype = new Person()
为什么不能改成:
Student.prototype = Person.prototype;
原因:
改了后,Student的原型对象中也有了sayHi方法,同时,Person的原型对象中也有了study方法,这样不合逻辑,继承就是,我有你的,你没我的;现在是你中有我,我中有你。