2019-12-05 JavaScript 變量與數(shù)據(jù)類型

Q1 JS 中有哪些數(shù)據(jù)類型?

數(shù)據(jù)類型包括(Undefined、Null、Boolean、NumberString),一種復雜數(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é)果如下:


image.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容