Q1 JS 中有哪些數(shù)據(jù)類型?
數(shù)據(jù)類型包括(Undefined、Null、Boolean、Number和String),一種復雜數(shù)據(jù)類型(Object)和一種新的原始數(shù)據(jù)類型(Symbol)。
下面是對數(shù)據(jù)類型的解釋:
Undefined
- 只有一個值,即undefined值
- 使用var聲明了變量,但未給變量初始化值,那么這個變量的值就是undefined
- 訪問一個對象不存在的屬性時,也會返回undefined
var a
console.log(a) // undefined
var obj = {
name: 'lisi',
// age: 20
}
console.log(obj.age) // undefined
Null
- 只有一個值,即 null 值
- null 值表示一個空對象指針
var a = null
console.log(typeof a) // object
Boolean
- 該類型只有兩個值,true 和 false
Number - Number類型包含整數(shù)和浮點數(shù)(浮點數(shù)數(shù)值必須包含一個小數(shù)點,且小數(shù)點后面至少有一位數(shù)字)兩種值
- NaN為非數(shù)字類型(Not a Number)
????1. 涉及到的 任何關(guān)于NaN的操作,都會返回NaN
????2. NaN不等于任何數(shù)值包括它本身
console.log(NaN === NaN) // false
console.log(NaN === true) // false
console.log(NaN === false) // false
console.log(NaN === null) // false
console.log(NaN === undefined) // false
console.log(NaN + 1) // NaN
console.log(NaN - 1) // NaN
console.log(NaN * 2) // NaN
console.log(NaN / 2) // NaN
console.log(NaN % 2) // NaN
console.log(99 % 0) // NaN
console.log(99 / 0) // Infinity
String
- 字符串有 length 屬性
- String類型用于表示由零或多個16位Unicode字符組成的字符序列,即字符串
- 字符串可以由單引號 ’ ’ 或雙引號 " " 表示
Object
- JS中對象是一組屬性與方法的集合
- 引用類型是一種數(shù)據(jù)結(jié)構(gòu),用于將數(shù)據(jù)和功能組織在一起
- 引用類型有時候也被稱為對象定義,因為它們描述的是一類對象所具有的屬性和方法
Symbol - 表示獨一無二的值
- Symbol值通過Symbol函數(shù)生成
????凡是屬性名屬于 Symbol 類型,就都是獨一無二的,可以保證不會與其他屬性名產(chǎn)生沖突
// 沒有參數(shù)的情況
let s1 = Symbol();
let s2 = Symbol();
s1 === s2 // false
// 有參數(shù)的情況
let s1 = Symbol('foo');
let s2 = Symbol('foo');
s1 === s2 // false
- Symbol函數(shù)前不能使用new命令
????因為生成的Symbol是一個原始類型的值,不是對象,不能添加屬性 - Symbol函數(shù)可以接受一個字符串作為參數(shù),表示對Symbol實例的描述
????為了在控制臺顯示,或者轉(zhuǎn)為字符串時,比較容易區(qū)分 - Symbol值可以作為標識符,用于對象的屬性名,能保證不會出現(xiàn)同名的屬性
????因為每一個 Symbol 值都是不相等的,能防止某一個鍵被不小心改寫或覆蓋 - Symbol 值不能與其他類型的值進行運算,會報錯
// Symbol 值不能與其他類型的值進行運算,會報錯
let sym = Symbol('My symbol');
"your symbol is " + sym
// TypeError: can't convert symbol to string
`your symbol is ${sym}`
// TypeError: can't convert symbol to string
- Symbol 值可以顯式轉(zhuǎn)為字符串和布爾值,但是不能轉(zhuǎn)為數(shù)值
// Symbol 值可以顯式轉(zhuǎn)為字符串和布爾值,但是不能轉(zhuǎn)為數(shù)值
let sym1 = Symbol('My symbol');
String(sym1) // 'Symbol(My symbol)'
sym1.toString() // 'Symbol(My symbol)'
let sym2 = Symbol();
Boolean(sym2) // true
!sym2 // false
if (sym2) {
// ...
}
Number(sym2) // TypeError
sym2 + 2 // TypeError
- 消除魔術(shù)字符串
????魔術(shù)字符串指的是,在代碼之中多次出現(xiàn)、與代碼形成強耦合的某一個具體的字符串或者數(shù)值。風格良好的代碼,應(yīng)該盡量消除魔術(shù)字符串,該由含義清晰的變量代替
// 常用的消除魔術(shù)字符串的方法,就是把它寫成一個變量
const shapeType = {
triangle: 'Triangle'
};
function getArea(shape, options) {
let area = 0;
switch (shape) {
case shapeType.triangle:
area = .5 * options.width * options.height;
break;
}
return area;
}
getArea(shapeType.triangle, { width: 100, height: 100 });
- 屬性名遍歷
????Symbol 作為屬性名,該屬性不會出現(xiàn)在for…in、for…of循環(huán)中,也不會被 Object.keys()、Object.getOwnPropertyNames()、JSON.stringify() 返回。但是,它也不是私有屬性,有一個Object.getOwnPropertySymbols方法,可以獲取指定對象的所有 Symbol 屬性名
????Object.getOwnPropertySymbols方法返回一個數(shù)組,成員是當前對象的所有用作屬性名的 Symbol 值
const obj = {};
let foo = Symbol("foo");
Object.defineProperty(obj, foo, {
value: "foobar",
});
for (let i in obj) {
console.log(i); // 無輸出
}
Object.getOwnPropertyNames(obj)
// []
Object.getOwnPropertySymbols(obj)
// [Symbol(foo)]
Q2 JS Object 數(shù)據(jù)類型和其它原始類型有什么區(qū)別?
上面的數(shù)據(jù)類型除了Object外,都是基本數(shù)據(jù)類型。
下面是基本數(shù)據(jù)類型與Object對象類型的區(qū)別
| 類型 | 原始類型 | 對象類型 |
|---|---|---|
| 值 | 不可改變 | 可以改變 |
| 屬性和方法 | 不能添加 | 能添加 |
| 存儲值 | 值 | 地址(指針) |
| 比較 | 值的比較 | 地址的比較 |
下面詳細講述一下:
1、基本數(shù)據(jù)類型的值不可改變,對象數(shù)據(jù)類型的值可以改變
let name = 'wanglin';
name.slice(0,2) // wa
console.log(name); // wanglin
name.toUpperCase(); // WANGLIN
console.log(name); // wanglin
在上例中我們定義了一個變量name并將一個字符串類型的值"wanglin"賦值給了name,可以看到,調(diào)用slice方法和toUpperCase方法時,返回的是一個新的字符串,跟原來的變量name沒有關(guān)系,而name的值始終沒有改變。那么下面這種情況呢?
let name = 'wanglin';
name = 'wanglin1'
console.log(name); // wanglin1
看到這里,可能有些讀者會認為,name的值不是改變了嗎?但其實并不是這樣的,這里的name只是一個指針,你所理解的改變只是name指針的指向從wanglin改為了wanglin1,但真正的值wanglin和wanglin1是不會改變的。我們說的基礎(chǔ)類型的值不會改變,是指wanglin或wanglin1不變,而不是name不變。
對象類型的值可以改變
let person = {
name:'wanglin'
}
person.name = 'wangmumu'; // 改變對象的屬性值
person.age = 22; // 新增一個屬性
console.log(person); // {name: "wangmumu", age: 22}
2、基本數(shù)據(jù)類型存儲的是值,對象數(shù)據(jù)類型存儲的是地址(指針)
let a = 1;
let b = a;
b = 2;
console.log(a); // 1,a的值不會因b的值的改變而受到影響
let c = [];
let d = c;
d.push(1);
console.log(c) // [1],c的值因d的值的改變而受到影響了
可以看到,當我們將一個值賦值給另一個變量時,如果是基本類型,則這個變量不會因另一個變量值的變化而受影響。但如果是對象類型,則會受到影響。其根本原因是,基本數(shù)據(jù)類型存儲的是值,對象數(shù)據(jù)類型存儲的是地址。
假設(shè)上例中的c的地址是#001,當我們將c賦值給d時,其實是把c的地址,也就是#001賦值給了d,這時d的地址和c一樣都是#001,改變d的同時,c也會受到影響了。
如果我們實際開發(fā)中不想受到這個問題的困擾,可以使用淺拷貝來解決這個問題。當然,一旦提到淺拷貝,就會發(fā)現(xiàn)淺拷貝的各種問題,這時就要考慮使用深拷貝了。
3.基本數(shù)據(jù)類型的比較是值的比較,對象數(shù)據(jù)類型的比較是地址的比較
let a = 1,b = 1;
console.log( a == b ); // true
let c = [],d = [];
console.log( c == d ); // false
可以看到,如果是對象類型,即使是完全相同的值,比較的時候也是false,是因為他們的地址是不同的。由此我們引發(fā)出一個思考,如果想比較兩個對象類型的值是否相同,我們該怎么辦呢?
這里提出一種思路解決,就是封裝一個函數(shù)來判斷兩個對象的值是否相等,函數(shù)的參數(shù)就是這兩個對象本身,在函數(shù)里把這兩個對象的值遍歷用來逐一比較。
function isObjEqual(o1,o2){
let props1 = Object.getOwnPropertyNames(o1);
let props2 = Object.getOwnPropertyNames(o2);
if (props1.length != props2.length) {
return false;
}
for (let i = 0,max = props1.length; i < max; i++) {
let propName = props1[i];
if (o1[propName] !== o2[propName]) {
return false;
}
}
return true;
}
Q3 談?wù)勀銓Α弊兞刻嵘暗睦斫?/h1>
變量提升:函數(shù)及變量的聲明都將被提升到函數(shù)的最頂部;變量可以在使用后聲明,也就是變量可以先使用再聲明。(變量賦值不會提升,有多個函數(shù)聲明的時候,是由最后面的函數(shù)聲明來替代前面的;函數(shù)提升的優(yōu)先級大于變量提升的優(yōu)先級,即函數(shù)提升在變量提升之上)
例子:
var name="Bob";
(function(){
if(typeof name=== 'undefined'){
name='Jack';
console.log('Goodbye'+name);
}else{
console.log('hello'+name);
}
})(); //立即執(zhí)行函數(shù)
請問執(zhí)行后打印出的值是:(A)
A. Hello Bob
B. Goodbye Jack
C. Hello Jack
D. Goodbye Bob
解釋:name為全局定義變量且被賦值,進入else
var name="Bob";
(function(){
if(typeof name=== 'undefined'){
var name='Jack';//此處增加name聲明
console.log('Goodbye'+name);
}else{
console.log('hello'+name);
}
})(); // D
解釋: 變量提升,相當于在if判斷之前定義name(與外層name不同,屬于函數(shù)內(nèi)局部變量) 但在if 中給name 賦值,如下:
var name="Bob";
(function(){
var name;
if(typeof name=== 'undefined'){
name='Jack';
console.log('Goodbye'+name);
}else{
console.log('hello'+name);
}
})();// Goodbye Jack
再次修改
(function(){
name='Jack';
if(typeof name=== 'undefined'){
console.log('Goodbye'+name);
}else{
console.log('hello'+name);
}
})(); // hello Jack
console.log("name:"+name);// name:Jack (未使用var,默認聲明為全局變量)
Q4 分別指出以下運算的執(zhí)行結(jié)果
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>js的3種引入方法</title>
<script type="text/javascript">
var result = typeof undefined;
document.write("typeof undefined = " + result + "<br/>");
result = typeof true;
document.write("typeof true = " + result + "<br/>");
result = typeof 42;
document.write("typeof 42 = " + result + "<br/>");
result = typeof "42";
document.write("typeof '42' = " + result + "<br/>");
result = typeof { lift: 42 };
document.write("typeof {lift:42} = " + result + "<br/>");
</script>
</head>
<body>
</body>
</html>
結(jié)果如下:
