一个前端知识点汇总综合了学习过程中的知识点,比如this、闭包、BFC、ES6等,如果大佬们觉得还可以的话,求个star啦!
call和apply
- 每个函数都包含两个非继承而来的方法:apply()和call()
- 用途相同,都是在特定的作用域中调用函数
- 接收参数方面不同,apply接收两个参数,一个是函数运行时的作用域(this),另一个是参数数组;call方法第一个参数与apply方法相同,但传递给函数的参数必须列举出来。
call()
方法调用一个函数,其具有一个指定的this值和分别提供的参数:
fun.call(thisArg, arg1, arg2, ...)
apply()
方法调用一个函数,其具有一个指定的this值,以及作为一个数组(或类似数组的对象)提供的参数:
fun.apply(thisArg, [argsArray])
举个栗子???:
var one = {
name: 'one',
sayName: function(age) {
console.log(`Hello, I'm ${this.name}, and I'm ${age} years old`)
}
}
var day = {
name: 'day'
}
one.sayName.call(day, 20) // Hello, I'm day, and I'm 20 years old
one.sayName.apply(day, [20]) // Hello, I'm day, and I'm 20 years old
fn.call(o)
的原理就是先通过o.m = fn
将fn作为o的某个临时属性m存储,然后执行m,执行完毕后将m属性删除。
大致就是这样:
day.fn = one.sayName
day.fn()
delete day.fn
所以可以模拟实现apply和call。
首先来看apply的模拟:
第一版
Function.prototype.applyOne = function() {
var context = arguments[0]
var args = arguments[1]
context.fn = this
eval(context.fn(args.join(','))) // args.join(',')返回的是string,所以需要进行一下特殊处理)
delete context.fn
}
one.sayName.applyOne(day, [20]) // Hello, I'm day, and I'm 20 years old
第二版
要注意到的是,若this传入的是null,或者不传入,则会默认是全局环境,并且apply是有返回值的。
Function.prototype.applyTwo = function() {
var context = arguments[0] || window
var args = arguments[1]
context.fn = this
if (args == void 0) {
return context.fn()
}
var result = eval(context.fn(args.join(','))) // args.join(',')返回的是string,所以需要进行一下特殊处理
delete context.fn
return result
}
var name = "oneday"
var one = {
name: 'one',
sayName: function(age) {
console.log(`Hello, I'm ${this.name}, and I'm ${age} years old`)
}
}
var day = {
name: 'day'
}
one.sayName.applyTwo(null, [20]) // Hello, I'm oneday, and I'm 20 years old
emmmm...有一个问题就是万一context里面本来就有fn属性怎么办呢...对于es6而言,可以将fn设置为一个独特的Symbol值,如下:
var fn1 = Symbol('aaa')
var fn2 = Symbol('aaa')
fn1 == fn2 // false
但是毕竟symbol是es6的特性啊,所以在es5中可以使用产生随机数的方法,例如:
var fn1 = 'o' + Math.random()
var fn2 = 'o' + Math.random()
接下来就是apply:
Function.prototype.callOne = function() {
var context = [].shift.applyTwo(arguments)
var args = [].slice.applyTwo(arguments) // 将剩下的参数作为数组传入啊
return this.applyTwo(context, args)
}
emmmm...第一个参数就是arguments的第一个(一般是this,或者没有),后面的参数就作为数组形式传入。
bind方法
bind方法创建一个新的函数,当被调用时,this值是传递给bind的第一个参数,它的参数是bind其他的参数和其原本的参数,返回的是由指定的this值和初始化参数改造的原函数拷贝。
fun.bind(thisArg[, arg1[, arg2[, ...]]])
实例:
var name = '2333'
function Person(name) {
this.name = name
this.sayName = function() {
setTimeout(function() {
console.log(`Hello, I'm ${this.name}`)
}, 1000)
}
}
var oneday = new Person('1111')
oneday.sayName() // Hello, I'm 2333
但是下面这样就是1111~
this.sayName = function() {
setTimeout(function() {
console.log(`Hello, I'm ${this.name}`)
}.bind(this), 1000)
}
var oneday = new Person('1111')
oneday.sayName() // Hello, I'm 1111
而且还有偏函数(Partial Functions),在mdn中是这么说的:
bind()
的另一个最简单的用法是使一个函数拥有预设的初始参数。这些参数作为bind()
的第二个参数跟在this后面,之后它们会被插入到目标函数的参数列表的开始位置,传递给绑定函数的参数会跟在它们的后面。
emmmm...对呀没看懂,于是就看例子啊...
function list() {
return Array.prototype.slice.call(arguments)
}
var list1 = list(1, 2, 3) // [1, 2, 3]
// 所以listFun是拥有预设参数(5, 6)的,作为预设参数跟在第一个参数this后面
var listFun = list.bind(undefined, 5, 6)
// 后面传入的参数会跟在预设参数的后面
var list2 = listFun(7) // [5, 6, 7]
var list3 = listFun(8, 9) // [5, 6, 8, 9]
bind的模拟实现:
第一版
Function.prototype.bindOne = function() {
var me = this // 缓存this
var argArray = Array.prototype.slice.call(arguments, 1)
return function() {
return me.apply(arguments[0], argArray)
}
}
但是上述的没有实现继续传参可以添加到原参数后的功能...所以有了第二版
第二版
Function.prototype.bindTwo = function() {
var context = arguments[0]
var me = this
var argArray = Array.prototype.slice.call(arguments, 1)
return function() {
var innerArgs = Array.prototype.slice.call(arguments)
var finalArgs = argArray.concat(innerArgs)
return me.apply(context, finalArgs)
}
}
bind返回的函数如果作为构造函数,这个构造函数搭配new关键字出现的话,bind绑定的this需要被忽略,但是参数还要继续传入。意思就是bind的绑定比new的优先级要低。而且要在函数体内判断调用bind方法的一定要是一个函数。
复习一下new的作用:
- 创建一个新对象
- 新对象继承了该函数的原型(因此this就指向了这个新对象)
- 为这个新对象添加属性和方法并返回这个新对象
var obj = {}
obj.__proto__ = Base.prototype
Base.call(obj)
第三版
Function.prototype.bindThree = function() {
if (typeof this !== 'function') {
throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable')
}
// context指要把this绑定到的目标函数
var context = arguments[0]
// 这里的this指向调用bind的函数
var me = this
var argArray = Array.prototype.slice.call(arguments, 1)
var F = function() {}
F.prototype = this.prototype
var bound = function() {
var innerArgs = Array.prototype.slice.call(arguments)
var finalArgs = argArray.concat(innerArgs)
// 如果调用bind的函数是F的实例,那么this就还是指向调用bind的函数,如果不是F的实例,那么this就进行改变
return me.apply(this instanceof F ? this : context, finalArgs)
}
bound.prototype = new F()
return bound
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。