实现 arr[-1] = arr[arr.length - 1]
这道题的意思是:提供一个createArr()
方法,用此方法创建的数组满足arr[-1] = arr[arr.length - 1]
:
function createArr (...elements) {
// ...代码
return arr
}
var arr1 = createArr(1, 2, 3)
console.log(arr1[-1]) // 3
console.log(arr1[-2]) // 2
解题思路:
其实对于这类题目,我首先想到的会是Object.defineProperty()
或者Proxy
。因为这里涉及到了对数组值的获取,显然用Proxy
是比较合适的。什么?你问我为什么不用Object.defineProperty()
?因为这个方法是针对于对象的某一个属性的呀,对数组来说不合适。
所以对于这道题,我们也许可以使用Proxy
代理每次传入进来的下标,也就是重写一下数组的get
方法,在这个方法中我们去处理这方面的逻辑,一起来看看代码吧😊:
function createArr (...elements) {
let handler = {
get (target, key, receiver) { // 第三个参数传不传都可以
let index = Number(key) // 或者 let index = ~~key
if (index < 0) {
index = String(target.length + index)
}
return Reflect.get(target, index, receiver)
}
}
let target = [...elements] // 创建一个新数组
return new Proxy(target, handler)
}
var arr1 = createArr(1, 2, 3)
console.log(arr1[-1]) // 3
console.log(arr1[-2]) // 2
注意点:
-
get
接收到的第二个参数key
表示的是数组下标,它是字符串形式,所以需要转为Number
,当然方法有很多种了,使用Number()
也可以,使用~~
双非按位取反运算符也可以。对比于Number()
的好处就是Number(undefined)
会转换为NaN
;但是使用~~
能保证一直是数字,~~undefined === 0
。(什么?你还不知道区别?那你得看霖呆呆的这篇文章了:JS中按位取反运算符~及其它运算符)
-
接下来只需要判断一下传入进来的下标是不是小于0的,小于0的话加上数组的长度就可以了
-
然后返回index
这一项使用的是Reflect.get(target, index)
。什么?为什么不直接用target[index]
?当然这样也可以,对比target[index]
的区别就是Reflect.get(target, index)
如果传入的target
不是一个Object
的话(数组也属于对象),就会报一个类型错误TypeError
,而target[index]
返回的会是一个undefined
。比如这样:
var obj = 5
console.log(obj['b']) // undefined
console.log(Reflect.get(obj, 'b')) // Uncaught TypeError: Reflect.get called on non-object
扩展点:
呆呆这边主要是想扩展一下get
的第三个参数receiver
(接受者),在MDN
上的解释是:
如果target
对象中指定了getter
,receiver
则为getter
调用时的this
值。
来看个例子理解一下😊。
案例一
例如我们开始有这么一个对象:
var obj = {
fn: function () {
console.log('lindaidai')
}
}
现在使用Proxy
来赋值到obj1
中:
var obj = {
fn: function () {
console.log('lindaidai')
}
}
var obj1 = new Proxy(obj, {
get (target, key, receiver) {
console.log(receiver === obj1) // true
console.log(receiver === target) // false
return target[key]
}
})
obj1.fn()
可以看到,receiver
表示的是obj1
这个新的代理对象,target
表示的是被代理的对象obj
。
所以,receiver
可以表示使用代理对象本身。
案例二
另一种情况,receiver
也可以表示是从其继承的对象。
var proxy = new Proxy({}, {
get (target, key, receiver) {
return receiver;
}
})
console.log(proxy.getReceiver === proxy) // true
var inherits = Object.create(proxy)
console.log(inherits.getReceiver === inherits) // true
这个案例中,我新建了一个空对象的代理对象proxy
,使用proxy.getReceiver
获取它的receiver
,发现它就是代理对象本身。
而如果我将这个代理对象作为一个原型对象,创建出一个新的对象inherits
,也就是实现了原型式继承,那么这时候receiver
的值就是这个被继承的对象inherits
。
总结:
- 可以使用
Proxy
代理,来改变每次获取数组的值。
Proxy
的get
中,第二个参数是字符串,即使传入的是数组下标。
- 对比于
Number()
的好处就是Number(undefined)
会转换为NaN
;但是使用~~
能保证一直是数字,~~undefined === 0
。
- 对比
target[index]
的区别就是Reflect.get(target, index)
如果传入的target
不是一个Object
的话(数组也属于对象),就会报一个类型错误TypeError
,而target[index]
返回的会是一个undefined
。
Proxy
的get
中,第三个参数receiver
通常为使用代理对象本身或从其继承的对象。