call
- call 的實現, 一個一個傳參
var foo = {
val: 1
}
function bar() {
console.log(this.val)
}
bar.call(foo) // 1
// 思路
var foo = {
var: 1,
bar: function() {
console.log(this.val)
}
}
foo.bar() // 1
// 第一版
Function.prototype.call = function(ctx) {
const context = ctx ? ctx : window
context.fn = this // foo.bar
context.fn()
delete context.fn
}
// 第二版,加引數
Function.prototype.call = function(ctx) {
const context = ctx ? ctx : window
context.fn = this
let args = []
for (var i = 1; i < arguments.length; i++) {
// es6
// args.push(arguments[i])
// eval
args.push('arguments[' + i + ']')
}
// es6
// context.fn(...args)
// eval
const res = eval('context.fn( ' + args + ')')
delete context.fn
return res
}
複製程式碼
apply
- apply 的實現 陣列傳參
思路,和apply雷同,只是處理引數的方式不一樣而已
Function.prototype.apply = function(ctx, arr) {
const context = ctx ? ctx : window
context.fn = this
let res
if (!arr) {
return context.fn()
} else {
let args = []
for (var i = 0; i < arr.length; i++) {
// 這裡同樣有兩種寫法,就不按個貼了
args.push(arr[i])
}
res = context.fn(...args)
}
delete context.fn
return res
}
複製程式碼
bind
- bind 的實現
說明: bing的返回函式是一個函式
例外: 1) 返回的函式也可以進行傳參
2) 當例項是通過返回函式new出來的時候,this會失效,但是引數還是照樣生效
// 第一版
Function.prototype.bind = function(ctx) {
const self = this
return function() {
return self.call(ctx)
}
}
// 新增引數處理,先不管返回函式傳參的情況
Function.prototype.bind = function(ctx) {
const self = this
// 從第二個開始取
const args = Array.prototype.slice.call(arguments, 1) // typeof Array
return function() {
return self.apply(ctx, args)
}
}
// 處理返回函式傳參
Function.prototype.bind = function(ctx) {
const self = this
// 從第二個開始取
const args1 = Array.prototype.slice.call(arguments, 1)
return function() {
const args2 = Array.prototype.slice.call(arguments)
return self.apply(ctx, args1.contact(args2))
}
}
// 處理new的情況,同時保留引數的引用
Function.prototype.bind = function(ctx) {
const self = this
// 從第二個開始取
const args1 = Array.prototype.slice.call(arguments, 1)
const resFn = function() {
const args2 = Array.prototype.slice.call(arguments)
return self.apply(this instanceof resFn ? this : cxt, args1.contact(args2))
}
resFn.prototype = this.prototype
return resFn
}
// 優化
Function.prototype.bind = function(ctx) {
if (typeof this !== 'function') {
throw new Error('The caller shou be a function!!!')
}
const self = this
// 從第二個開始取
const args1 = Array.prototype.slice.call(arguments, 1)
const resFn = function() {
const args2 = Array.prototype.slice.call(arguments)
return self.apply(this instanceof resFn ? this : cxt, args1.contact(args2))
}
return resFn
}
複製程式碼
new
- new 關鍵字的實現
思路: new出來的物件,可以訪問建構函式中的變數、方法和例項原型上的變數、方法。
可以新建一個物件,該物件的_proto_ 指向例項原型, 這個就可以訪問原型上的變數、方法
該變物件的this指向,使其能訪問建構函式的變數和物件
function createNew() {
let obj = new Object()
// constructor 是建構函式
let constructor = [].shift.call(arguments)
// constructor.prototype 是例項原型
obj.__proto__ = constructor.prototype
// 改變this指向
constructor.apply(obj, arguments)
return obj
}
// 問題: 當有返回值的時候
1) 返回值為字串
2) 返回值為物件
第一種情況:
function test(name, age) {
this.name = name
this.age = age
return 'test'
}
const obj = new test('len', 23)
console.log(obj.name) // undefined
console.log(obj.age) // undefined
第二種情況:
function test(name, age) {
this.name = name
this.age = age
return {
name,
age
}
}
const obj = new test('len', 23)
console.log(obj.name) // len
console.log(obj.age) // 23
改進:
function createNew() {
let obj = new Object()
let constructor = [].shift.call(arguments)
obj.__proto__ = constructor.prototype
const res = constructor.apply(obj, arguments)
return typeof res === 'object' ? res : obj
}
複製程式碼