組和對象
什么是對象,其實就是一種類型,即引用類型。而對象的值就是引用類型的實例。在ECMAScript 中引用類型是一種數據結構,用于將數據和功能組織在一起。它也常被稱做為類,但 ECMAScript 中卻沒有這種東西。雖然 ECMAScript 是一門面向對象的語言,卻不具備傳統面向對象語言所支持的類和接口等基本結構。
一.Object 類型
到目前為止,我們使用的引用類型最多的可能就是 Object 類型了。雖然 Object 的實例不具備多少功能,但對于在應用程序中的存儲和傳輸數據而言,它確實是非常理想的選擇。創建 Object 類型有兩種。一種是使用 new 運算符,一種是字面量表示法。
1.使用 new 運算符創建 Object
var box = new Object();
box.name = '大圣網絡';
box.age = 28;
2.new 關鍵字可以省略
var box = Object();
3.使用字面量方式創建 Object
var box = {
name : '大圣網絡',
age : 28
};
4.屬性字段也可以使用字符串形式
var box = {
'name' : '大圣網絡',
'age' : 28
};
5.使用字面量及賦值方式
var box = {}; //字面量方式聲明空的對象
box.name = '大圣網絡'; //點符號給屬性復制
box.age = 28;
6.兩種屬性輸出方式
alert(box.age);
alert(box['age']);
7.給對象創建方法
var box = {
run : function () { //對象中的方法
return '運行';
}
}
alert(box.run()); //調用對象中的方法
8.使用 delete 刪除對象屬性
delete box.name; //刪除屬性
在實際開發過程中,一般我們更加喜歡字面量的聲明方式。因為它清晰,語法代碼少 ,而且還給人一種封裝的感覺。字面量也是向函數傳遞大量可選參數的首選方式。
function box(obj) { //參數是一個對象
if (obj.name != undefined) alert(obj.name); //判斷屬性是否存在
if (obj.age != undefined) alert(obj.age);
}
box({ //調用函數傳遞一個對象
name : '大圣',
age : 28
});
二. Array 類型
除了 Object 類型之外,Array 類型是 ECMAScript 最常用的類型。而且 ECMAScript 中的 Array 類型和其他語言中的數組有著很大的區別。 雖然數組都是有序排列, 但 ECMAScript中的數組每個元素可以保存任何類型。ECMAScript 中數組的大小也是可以調整的。創建 Array 類型有兩種方式:第一種是 new 運算符,第二種是字面量。
1.使用 new 關鍵字創建數組
var box = new Array(); //創建了一個數組
var box = new Array(10); //創建一個包含 10 個元素的數組
var box = new Array('李炎恢',28,'教師','鹽城'); //創建一個數組并分配好了元素
2.以上三種方法,可以省略 new 關鍵字。
var box =Array();
3 使用字面量方式創建數組
var box = []; //創建一個空的數組
var box = ['大圣',28,'金箍棒']; //創建包含元素的數組
4.使用索引下標來讀取數組的值
alert(box[2]); //獲取第三個元素
box[2] = '學生'; //修改第三個元素
box[4] = '計算機編程'; //增加第五個元素
5.使用 length 屬性獲取數組元素量
alert(box.length) //獲取元素個數
box.length = 10; //強制元素個數
box[box.length] = 'JS 技術'; //通過 length 給數組增加一個元素
6.創建一個稍微復雜一點的數組
var box = [
{
name : '李炎恢',
age : 28,
run : function () {
return 'run 了';
}
},
['馬云','馬化騰',new Object()],
'江蘇',
25+25,
new Array(1,2,3)
];
alert(box);
三. 對象中的方法
轉換方法
對象或數組都具有toLocaleString()、 toString()和valueOf()方法。 其中toString()和valueOf()無論重寫了誰,都會返回相同的值。數組會講每個值進行字符串形式的拼接,以逗號隔開。
var box = ['小學生',8,'計算機編程']; //字面量數組
alert(box); //隱式調用了 toString()
alert(box.toString()); //和 valueOf()返回一致
alert(box.toLocaleString()); //返回值和上面兩種一致
默認情況下,數組字符串都會以逗號隔開。如果使用 join()方法(相當于php函數的implode),則可以使用不同的分隔符來構建這個字符串。
var box = ['小學生', 8, '計算機編程'];
alert(box.join('|')); //小學生|8|計算機編程
棧方法
ECMAScript 數組提供了一種讓數組的行為類似于其他數據結構的方法。也就是說,可以讓數組像棧一樣,可以限制插入和刪除項的數據結構。棧是一種數據結構(后進先出),也就是說最新添加的元素最早被移除。而棧中元素的插入(或叫推入)和移除(或叫彈出),只發生在一個位置——棧的頂部。ECMAScript 為數組專門提供了 push()和 pop()方法。push()方法可以接收任意數量的參數,把它們逐個添加到數組的末尾,并返回修改后數組的長度。而 pop()方法則從數組末尾移除最后一個元素,減少數組的 length 值,然后返回移除的元素。
var box = ['大圣', 500, '打妖怪']; //字面量聲明
alert(box.push('斗戰勝佛')); //數組末尾添加一個元素,并且返回長度
alert(box); //查看數組
box.pop(); //移除數組末尾元素,并返回移除的元素
alert(box); //查看元素
隊列方法
棧方法是后進先出,而列隊方法就是先進先出。列隊在數組的末端添加元素,從數組的前端移除元素。通過 push()向數組末端添加一個元素,然后通過 shift()方法從數組前端移除一個元素。ECMAScript 還為數組提供了一個 unshift()方法,它和 shift()方法的功能完全相反。
var box = ['大圣', 500, '打妖怪'];
alert(box.shift()); //移除數組開頭元素,并返回移除的元素
alert(box); //查看數組
var box = ['大圣', 500, '打妖怪'];
alert(box.unshift('a','b')); //數組開頭添加兩個元素
alert(box); //查看數組
alert(box.pop()); //移除數組末尾元素,并返回移除的元素
alert(box); //查看數組
重排序方法
數組中已經存在兩個可以直接用來排序的方法:reverse()和 sort()。
reverse() 逆向排序
var box = [1,2,3,4,5]; //數組
alert(box.reverse()); //逆向排序方法,返回排序后的數組
alert(box); //源數組也被逆向排序了,說明是引用
sort() 從小到大排序
var box = [4,1,7,3,9,2]; //數組
alert(box.sort()); //從小到大排序,返回排序后的數組
alert(box); //源數組也被從小到大排序了
sort 方法的默認排序在數字排序上有些問題,因為數字排序和數字字符串排序的算法是一樣的。我們必須修改這一特征,修改的方式,就是給 sort(參數)方法傳遞一個函數參數。這點可以參考手冊說明。
操作方法
ECMAScript 為操作已經包含在數組中的元素提供了很多方法。 concat()方法可以基于當前數組創建一個新數組。 slice()方法可以基于當前數組獲取指定區域元素并創建一個新數組 。splice()主要用途是向數組的中部插入元素。
var box = ['大圣網絡', 28, '中國']; //當前數組
var box2 = box.concat('計算機編程'); //創建新數組,并添加新元素
alert(box2); //輸出新數組
var box = ['大圣網絡', 28, '中國'];; //當前數組
var box2 = box.slice(1); //box.slice(1,3),2-4 之間的元素
alert(box2); //28,中國
splice 中的刪除功能:
var box = ['大圣網絡', 28, '中國']; //當前數組
var box2 = box.splice(0,2); //截取前兩個元素
alert(box2); //返回截取的元素
alert(box); //當前數組被截取的元素被刪除
splice 中的插入功能:
var box = ['大圣網絡', 28, '中國']; //當前數組
var box2 = box.splice(1,0,'計算機編程','安徽'); //沒有截取,但插入了兩條
alert(box2); //在第 2 個位置插入兩條
alert(box); //輸出
splice 中的替換功能:
var box = ['大圣網絡', 28, '中國']; //當前數組
var box2 = box.splice(1,1,100); //截取了第 2 條,替換成 100
alert(box2); //輸出截取的 28
alert(box); //輸出數組
Array 對象
Array 對象用于在變量中存儲多個值:
var cars = ["Saab", "Volvo", "BMW"];
第一個數組元素的索引值為 0,第二個索引值為 1,以此類推。
更多有關JavaScript Array參考手冊請參考 JavaScript Array 對象手冊。
Array 對象屬性
方法 | 描述 |
---|---|
concat() | 連接兩個或更多的數組,并返回結果。 |
every() | 檢測數值元素的每個元素是否都符合條件。 |
filter() | 檢測數值元素,并返回符合條件所有元素的數組。 |
indexOf() | 搜索數組中的元素,并返回它所在的位置。 |
join() | 把數組的所有元素放入一個字符串。 |
lastIndexOf() | 返回一個指定的字符串值最后出現的位置,在一個字符串中的指定位置從后向前搜索。 |
map() | 通過指定函數處理數組的每個元素,并返回處理后的數組。 |
pop() | 刪除數組的最后一個元素并返回刪除的元素。 |
push() | 向數組的末尾添加一個或更多元素,并返回新的長度。 |
reverse() | 反轉數組的元素順序。 |
shift() | 刪除并返回數組的第一個元素。 |
slice() | 選取數組的的一部分,并返回一個新數組。 |
some() | 檢測數組元素中是否有元素符合指定條件。 |
sort() | 對數組的元素進行排序。 |
splice() | 從數組中添加或刪除元素。 |
toString() | 把數組轉換為字符串,并返回結果。 |
unshift() | 向數組的開頭添加一個或更多元素,并返回新的長度。 |
valueOf() | 返回數組對象的原始值。 |
如您還有不明白的可以在下面與我留言或是與我探討QQ群308855039,我們一起飛!
我們知道在JS中對象和數組的操作方式是不一樣的,但是我們可以通過封裝,給對象加一層包裝器,讓它可以和數組擁有同樣的使用方式。我們主要借助Object.keys()、Object.values()、Object.entries()、Proxy。
看一下MDN上的解釋:
Object.keys() 方法會返回一個由一個給定對象的自身可枚舉屬性組成的數組,數組中屬性名的排列順序和正常循環遍歷該對象時返回的順序一致。
也就是Object.keys可以獲取對象的所有屬性名,并生成一個數組。
var obj = { a: 0, b: 1, c: 2 };
console.log(Object.keys(obj));
// console: ['a', 'b', 'c']
看一下MDN上的解釋:
Object.values()方法返回一個給定對象自身的所有可枚舉屬性值的數組,值的順序與使用for...in循環的順序相同 ( 區別在于 for-in 循環枚舉原型鏈中的屬性 )。
Object.values()返回一個數組,元素是對象上找到的可枚舉屬性值。
var obj = { foo: 'bar', baz: 42 };
console.log(Object.values(obj));
// ['bar', 42]
看一下MDN上的解釋:
Object.entries()方法返回一個給定對象自身可枚舉屬性的鍵值對數組,其排列與使用 for...in 循環遍歷該對象時返回的順序一致(區別在于 for-in 循環還會枚舉原型鏈中的屬性)。
Object.entries()返回一個數組,元素是由屬性名和屬性值組成的數組。
const obj = { foo: 'bar', baz: 42 };
console.log(Object.entries(obj));
// [ ['foo', 'bar'], ['baz', 42] ]
Proxy是JS最新的對象代理方式,用于創建一個對象的代理,從而實現基本操作的攔截和自定義(如屬性查找、賦值、枚舉、函數調用等)。
使用Proxy可以封裝對象的原始操作,在執行對象操作的時候,會經過Proxy的處理,這樣我們就可以實現數組操作命令。
const handler = {
get: function(obj, prop) {
return prop in obj ? obj[prop] : 37;
}
}
const p = new Proxy({}, handler);
p.a = 1;
p.b = undefined;
console.log(p.a, p.b)
console.log('c' in p, p.c)
以上示例的中,當對象中不存在屬性名時,默認返回值為37
使用Proxy包裝原生對象生成一個代理對象p,對代理對象的操作會轉發到原生對象上。
let target = {};
let p = new Proxy(target, {});
p.a = 37; // 操作轉發到目標
console.log(target.a); // 37. 操作已經被正確地轉發
我們要實現以下幾個函數:forEach、map、filter、reduce、slice、find、findKey、includes、keyOf、lastKeyOf。
數組中的forEach函數定義:arr.forEach(callback(currentValue [, index [, array]])[, thisArg])
數組中的forEach需要傳入一個函數,函數的第一個參數是當前操作的元素值,第二個參數是當前操作的元素索引,第三個參數是正在操作的對象。
對于對象,我們將參數定為:currentValue、key、target。我們可以使用Object.keys來遍歷對象。
Object.keys(target).forEach(key => callback(target[key], key, target))
這里需要target和callback參數,我們通過函數封裝一下
function forEach(target, callback) {
Object.keys(target).forEach(key => callback(target[key], key, target))
}
這樣我們就可以使用以下方式調用:
const a = {a: 1, b: 2, c: 3}
forEach(a, (v, k) => console.log(`${k}-${v}`))
// a-1
// b-2
// c-3
通過Proxy封裝:
const handler = {
get: function(obj, prop) {
return forEach(obj)
}
}
const p = new Proxy(a, handler)
p.forEach((v, k) => console.log(`${k}-${v}`))
以上方式當然是不行的,我們主要看最后一句,其執行方式和數組的forEach完全相同,我們在調用Proxy封裝的對象時,獲取數據時,會調用get函數,第一個參數為原生對象,第二個參數為屬性名-forEach,在這里就要修改我們的forEach函數了。首先p.forEach的參數是一個函數,因此我們代理對象的返回值需要接收一個函數作為參數,因此修改如下:
function forEach(target) {
return (callback) => Object.keys(target).forEach(key => callback(target[key], key, target))
}
因此完成代碼為:
function forEach(target) {
return (callback) => Object.keys(target).forEach(key => callback(target[key], key, target))
}
const handler = {
get: function(obj, prop) {
return forEach(obj)
}
}
const a = {a: 1, b: 2, c: 3}
const p = new Proxy(a, handler)
p.forEach((v, k) => console.log(`${k}-${v}`))
// a-1
// b-2
// c-3
我們應該把以上代碼封裝為模塊,方便對外使用:
const toKeyedArray = (obj) => {
const methods = {
forEach(target) {
return (callback) => Object.keys(target).forEach(key => callback(target[key], key, target));
}
}
const methodKeys = Object.keys(methods)
const handler = {
get(target, prop) {
if (methodKeys.includes(prop)) {
return methods[prop](target)
}
return Reflect.get(...arguments)
}
}
return new Proxy(obj, handler)
}
const a = { a: 1, b: 2, c: 3}
const p = toKeyedArray(a)
p.forEach((v, k) => console.log(`${k}-${v}`))
以上是forEach的實現和封裝,其他函數的實現方式類似。
全部源碼如下:
const toKeyedArray = (obj) => {
const methods = {
forEach(target) {
return (callback) => Object.keys(target).forEach(key => callback(target[key], key, target));
},
map(target) {
return (callback) =>
Object.keys(target).map(key => callback(target[key], key, target));
},
reduce(target) {
return (callback, accumulator) =>
Object.keys(target).reduce(
(acc, key) => callback(acc, target[key], key, target),
accumulator
);
},
forEach(target) {
return callback =>
Object.keys(target).forEach(key => callback(target[key], key, target));
},
filter(target) {
return callback =>
Object.keys(target).reduce((acc, key) => {
if (callback(target[key], key, target)) acc[key] = target[key];
return acc;
}, {});
},
slice(target) {
return (start, end) => Object.values(target).slice(start, end);
},
find(target) {
return callback => {
return (Object.entries(target).find(([key, value]) =>
callback(value, key, target)
) || [])[0];
};
},
findKey(target) {
return callback =>
Object.keys(target).find(key => callback(target[key], key, target));
},
includes(target) {
return val => Object.values(target).includes(val);
},
keyOf(target) {
return value =>
Object.keys(target).find(key => target[key] === value) || null;
},
lastKeyOf(target) {
return value =>
Object.keys(target)
.reverse()
.find(key => target[key] === value) || null;
}
}
const methodKeys = Object.keys(methods)
const handler = {
get(target, prop) {
if (methodKeys.includes(prop)) {
return methods[prop](target)
}
const [keys, values] = [Object.keys(target), Object.values(target)];
if (prop === 'length') return keys.length;
if (prop === 'keys') return keys;
if (prop === 'values') return values;
if (prop === Symbol.iterator)
return function* () {
for (value of values) yield value;
return;
};
return Reflect.get(...arguments)
}
}
return new Proxy(obj, handler)
}
const x = toKeyedArray({ a: 'A', b: 'B' });
x.a; // 'A'
x.keys; // ['a', 'b']
x.values; // ['A', 'B']
[...x]; // ['A', 'B']
x.length; // 2
// Inserting values
x.c = 'c'; // x = { a: 'A', b: 'B', c: 'c' }
x.length; // 3
// Array methods
x.forEach((v, i) => console.log(`${i}: ${v}`)); // LOGS: 'a: A', 'b: B', 'c: c'
x.map((v, i) => i + v); // ['aA', 'bB, 'cc]
x.filter((v, i) => v !== 'B'); // { a: 'A', c: 'c' }
x.reduce((a, v, i) => ({ ...a, [v]: i }), {}); // { A: 'a', B: 'b', c: 'c' }
x.slice(0, 2); // ['A', 'B']
x.slice(-1); // ['c']
x.find((v, i) => v === i); // 'c'
x.findKey((v, i) => v === 'B'); // 'b'
x.includes('c'); // true
x.includes('d'); // false
x.keyOf('B'); // 'b'
x.keyOf('a'); // null
x.lastKeyOf('c'); // 'c'
JS系列1-布爾陷阱以及如何避免
*請認真填寫需求信息,我們會在24小時內與您取得聯系。