36 Array.prototype.toString()
36.1 基本介绍
toString
方法返回一个字符串,表示指定的数组及其元素。
toString()
输入参数:无。
输出:表示数组元素的字符串。
注意事项:
Array
对象覆盖了Object
的toString
方法,内部调用了join
方法,使用,
来拼接数组元素。- 如果join方法不可用会使用
Object.prototype.toString()
方法来替代。 - 当数组需要表示为文本值的时候会自动调用
toString()
方法。
36.2 手写实现
MyArray.prototype.toString = function(){
if(typeof this.join !== "function"){
return Object.prototype.toString.call(this)
}else{
return this.join()
}
}
const arr = new MyArray()
arr.join = 1; // 将 `join` 重新赋值为非函数的值
console.log(arr.toString()); // [object Array]
const arr_1 = new MyArray(1, 2, "a", "1a");
console.log(arr_1.toString()); // "1,2,a,1a"
console.log(MyArray.prototype.toString.call({ join: () => 1 }));
// 1; 一个数字
console.log(MyArray.prototype.toString.call({ join: () => undefined }));
// undefined
console.log(MyArray.prototype.toString.call({ join: "not function" }));
// "[object Object]"
难点总结:
- 方法选择:根据
join
是否是函数来判断调用其他函数来实现,使用Object
原型上的toString
或者调用自身的join
方法。
37 unshift
37.1 基本介绍
unshift
方法将指定元素添加到数组的开头,并返回数组的新长度。
unshift()
unshift(element1)
unshift(element1, element2)
unshift(element1, element2, /* …, */ elementN)
输入参数:elementN
是添加到数组开头的元素
输出:返回添加元素后的length
属性。
注意事项:
unshift
与push
有类似的行为,但是将其元素插入到末尾。- 方法是
通用的
。
37.2 手写实现
MyArray.prototype.unshift = function(...args){
const newCount = args.length
const len = this.length ? this.length : 0
for(let i = this.length; i >= 0; i --){
if(i in this){
this[i + newCount] = this[i]
}else{
delete this[i + newCount]
}
}
for(let i = 0; i < newCount; i ++){
this[i] = args[i]
}
this.length = len + newCount
return this.length
}
// 测试示例
let arr_2 = new MyArray(4, 5, 6);
arr_2.unshift(1, 2, 3);
console.log(arr_2);
// [1, 2, 3, 4, 5, 6]
let arr_3 = new MyArray(4, 5, 6);
arr_3.unshift(1);
arr_3.unshift(2);
arr_3.unshift(3);
console.log(arr_3);
// [3, 2, 1, 4, 5, 6]
const arrayLike = {
length: 3,
unrelated: "foo",
2: 4,
};
MyArray.prototype.unshift.call(arrayLike, 1, 2);
console.log(arrayLike);
// { '0': 1, '1': 2, '4': 4, length: 5, unrelated: 'foo' }
const plainObj = {};
// 这里没有长度属性,所以这里的长的为 0
MyArray.prototype.unshift.call(plainObj, 1, 2);
console.log(plainObj);
// { '0': 1, '1': 2, length: 2 }
难点总结:
- 正确处理length:有的类数组对象可能不会携带length属性,此时要判断length的合法性。统计加入元素的个数,从而计算出正确的长度在最后返回。
- 正确的移动数组:根据插入的元素个数来正确的移动数组,遇到空槽需要正确的
delete this[i + newCount]
。
38
38.1 基本介绍
values()
方法返回一个新的数组迭代器对象,该对象迭代数组中每个元素的值。
values()
输入参数:无。
输出:一个新的可迭代迭代器对象。
注意事项:
Array.prototype.values()
是Array.prototype[Symbol.iterator]()
的默认实现。- 空槽会被处理为
undefined
。 - 生成的迭代器对象是一次性的,当状态
done
为true
的就不该再次使用。
38.2 手写实现
MyArray.prototype.values = function(){
if(Array.isArray(this)){
return this[Symbol.iterator]()
}else {
const arrLike = this
let index = 0
return {
next(){
if(index < arrLike.length){
const res = {value: arrLike[index ++], done: false}
return res
}else{
return {value: undefined, done: true}
}
},
[Symbol.iterator]:function(){
return this
}
}
}
}
// 测试代码
const arr_4 = new MyArray("a", "b", "c", "d", "e");
const iterator = arr_4.values();
for (const letter of iterator) {
console.log(letter);
} // "a" "b" "c" "d" "e"
const arrayLike_1 = {
length: 3,
0: "a",
1: "b",
2: "c",
};
for (const entry of MyArray.prototype.values.call(arrayLike_1)) {
console.log(entry);
}
// a
// b
// c
难点总结:
- 类数组对象处理:类数组对象一般没有实现迭代器协议和迭代器方法,我们需要返回一个可迭代的像(实现next方法,且自身实现迭代器协议)
- 数组返回:注意返回的是调用迭代器协议下迭代器方法返回的迭代器,不要返回整个迭代器方法。
39 Array.prototype.with()
39.1 基本介绍
with
方法是使用方括号表示法修改指定索引值的复制方法版本。它会返回一个新数组,其指定索引处的值会被新值替换。
arrayInstance.with(index, value)
输入参数:index
要修改的数组索引,越界会有RangeError
。value
要分配给指定索引的值。
输出:一个替换了index
索引的值的全新数组。
注意事项:
index > array.length
或index < -array.length
时抛出RangeError
。- 对于稀疏数组,将会把空槽视为
undefined
39.2 手写实现
MyArray.prototype.with = function(index, value){
if(index <= -this.length || index > this.length){
throw RangeError("index out of Range")
}
index = index < 0 ? index + this.length : index
const res = new MyArray()
for(let i = 0; i < this.length; i ++){
if(i === index){
res[i] = value
}else{
res[i] = this[i]
}
}
return res
}
const arr_5 = new MyArray(1, 2, 3, 4, 5);
console.log(arr_5.with(2, 6)); // [1, 2, 6, 4, 5]
console.log(arr_5); // [1, 2, 3, 4, 5]
const arrayLike_2 = {
length: 3,
unrelated: "foo",
0: 5,
2: 4,
3: 3, // 由于 length 属性的值为 3,with() 会忽略该值
};
console.log(MyArray.prototype.with.call(arrayLike_2, 0, 1));
// [ 1, undefined, 4 ]
难点总结:
- 范围错误:当范围不合法时抛出错误,这和之前很多索引处理不同。
- 返回全新数组:不修改原数组,而是返回一个全新的数组。