1. 程式人生 > >JS 資料型別、賦值、深拷貝和淺拷貝

JS 資料型別、賦值、深拷貝和淺拷貝

js 資料型別

  1. 六種 基本資料型別:
  • Boolean. 布林值,true 和 false.
  • null. 一個表明 null 值的特殊關鍵字。 JavaScript 是大小寫敏感的,因此 null 與 Null、NULL或其他變數完全不同。
  • undefined. 變數未定義時的屬性。
  • Number. 表示數字,例如: 42 或者 3.14159。
  • String. 表示字串,例如:"Howdy"
  • Symbol ( 在 ECMAScript 6 中新新增的型別).。一種資料型別,它的例項是唯一且不可改變的。
  1. 以及 Object 物件引用資料型別

大多數情況下,我們可以通過typeof屬性來判斷。只不過有一些例外,比如:

var fn = new Function ('a', 'b', 'return a + b')

typeof fn // function

基本型別 和 引用資料型別 的相關區別

基本資料型別

我們來看一下 MDN 中對基本資料型別的一些定義:

除 Object 以外的所有型別都是不可變的(值本身無法被改變)。例如,與 C 語言不同,JavaScript 中字串是不可變的(譯註:如,JavaScript 中對字串的操作一定返回了一個新字串,原始字串並沒有被改變)。我們稱這些型別的值為“原始值”。

var a = 'string'
a[0] = 'a'
console.log(a)  // string

我們通常情況下都是對一個變數重新賦值,而不是改變基本資料型別的值。在 js 中是沒有方法是可以改變布林值和數字的。倒是有很多操作字串的方法,但是這些方法都是返回一個新的字串,並沒有改變其原有的資料。比如:

  • 獲取一個字串的子串可通過選擇個別字母或者使用 String.substr().
  • 兩個字串的連線使用連線操作符 (+) 或者 String.concat().

引用資料型別

引用型別(object)是存放在堆記憶體中的,變數實際上是一個存放在棧記憶體的指標,這個指標指向堆記憶體中的地址。每個空間大小不一樣,要根據情況開進行特定的分配,例如。

var person1 = {name:'jozo'};
var person2 = {name:'xiaom'};
var person3 = {name:'xiaoq'};

引用型別的值是可變的:

person1['name'] = 'muwoo'

console
.log(person1) // {name: 'muwoo'}

傳值與傳址

瞭解了基本資料型別與引用型別的區別之後,我們就應該能明白傳值與傳址的區別了。
在我們進行賦值操作的時候,基本資料型別的賦值(=)是在記憶體中新開闢一段棧記憶體,然後再把再將值賦值到新的棧中。例如:

var a = 10;
var b = a;

a ++ ;
console.log(a); // 11
console.log(b); // 10

所以說,基本型別的賦值的兩個變數是兩個獨立相互不影響的變數。

但是引用型別的賦值是傳址。只是改變指標的指向,例如,也就是說引用型別的賦值是物件儲存在棧中的地址的賦值,這樣的話兩個變數就指向同一個物件,因此兩者之間操作互相有影響。例如:

var a = {}; // a儲存了一個空物件的例項
var b = a;  // a和b都指向了這個空物件

a.name = 'jozo';
console.log(a.name); // 'jozo'
console.log(b.name); // 'jozo'

b.age = 22;
console.log(b.age);// 22
console.log(a.age);// 22

console.log(a == b);// true

淺拷貝

先來看一段程式碼的執行:

var obj = {a: 1, b: {c: 2}}
var obj1 = obj
var obj2 = shallowCopy(obj);
functionshallowCopy(src) {
    var dst = {};
     for (var prop in src) {
         if (src.hasOwnProperty(prop)) {
             dst[prop] = src[prop];
          }
      }
     return dst;
}

var obj3 = Object.assign({}, obj)

obj.a = 2
obj.b.c = 3

console.log(obj) // {a: 2, b: {c: 3}}
console.log(obj1) // {a: 2, b: {c: 3}}
console.log(obj2) // {a: 1, b: {c: 3}}
console.log(obj3) // {a: 1, b: {c: 3}}

這段程式碼可以說明賦值得到的物件 obj1 只是將指標改變,其引用的仍然是同一個物件,而淺拷貝得到的的 obj2 則是重新建立了新物件。但是,如果原物件obj中存在另一個物件,則不會對物件做另一次拷貝,而是隻複製其變數物件的地址。這是因為淺拷貝只複製一層物件的屬性,並不包括物件裡面的為引用型別的資料。
對於陣列,更長見的淺拷貝方法便是slice(0)和 concat()
ES6 比較常見的淺拷貝方法便是 Object.assign

深拷貝

通過上面的這些說明,相信你對深拷貝大致瞭解了是怎樣一個東西了:深拷貝是對物件以及物件的所有子物件進行拷貝。那麼如何實現這樣一個深拷貝呢?

1. JSON.parse(JSON.stringify(obj))

對於常規的物件,我們可以通過JSON.stringify來講物件轉成一個字串,然後在用JSON.parse來為其分配另一個儲存地址,這樣可以解決記憶體地址指向同一個的問題:

var obj = {a: {b: 1}}
var copy = JSON.parse(JSON.stringify(obj))

obj.a.b = 2
console.log(obj) // {a: {b: 2}}
console.log(copy) // {a: {b: 1}}

但是 JSON.parse()JSON.stringify也存在一個問題,JSON.parse()和J SON.stringify()能正確處理的物件只有Number、String、Array等能夠被 json 表示的資料結構,因此函式這種不能被 json 表示的型別將不能被正確處理。

var target = {
    a: 1,
    b: 2,
    hello: function() { 
            console.log("Hello, world!");
    }
};
var copy = JSON.parse(JSON.stringify(target));
console.log(copy);   // {a: 1, b: 2}
console.log(JSON.stringify(target)); // "{"a":1,"b":2}"

2. 遍歷實現屬性複製

既然淺拷貝只能實現非object第一層屬性的複製,那麼遇到object只需要通過遞迴實現淺拷貝其中內部的屬性即可:

functionextend (source) {
  var target
  if (typeof source === 'object') {
    target = Array.isArray(source) ? [] : {}
    for (var key in source) {
      if (source.hasOwnProperty(key)) {
        if (typeof source[key] !== 'object') {
          target[key] = source[key]
        } else {
          target[key] = extend(source[key])
        }
      }
    }
  } else {
    target = source
  }
  return target
}

var obj1 = {a: {b: 1}}
var cpObj1 = extend(obj1)
obj1.a.b = 2
console.log(cpObj1) // {a: {b: 1}}

var obj2 = [[1]]
var cpObj2 = extend(obj2) 
obj2[0][0] = 2
console.log(cpObj2) // [[1]]

我們再來看一下 Zepto 中深拷貝的程式碼:

    // 內部方法:使用者合併一個或多個物件到第一個物件
    // 引數:
    // target 目標物件  物件都合併到target裡
    // source 合併物件
    // deep 是否執行深度合併
    functionextend(target, source, deep) {
        for (key in source)
            if (deep && (isPlainObject(source[key]) || isArray(source[key]))) {
                // source[key] 是物件,而 target[key] 不是物件, 則 target[key] = {} 初始化一下,否則遞迴會出錯的
                if (isPlainObject(source[key]) && !isPlainObject(target[key]))
                    target[key] = {}

                // source[key] 是陣列,而 target[key] 不是陣列,則 target[key] = [] 初始化一下,否則遞迴會出錯的
                if (isArray(source[key]) && !isArray(target[key]))
                    target[key] = []
                // 執行遞迴
                extend(target[key], source[key], deep)
            }
            // 不滿足以上條件,說明 source[key] 是一般的值型別,直接賦值給 target 就是了
            else if (source[key] !== undefined) target[key] = source[key]
    }

內部實現其實也是差不多。

http://www.vxjezfv.cn/
http://news.vxjezfv.cn/
http://www.xibiyo.com.cn/
http://news.xibiyo.com.cn/
http://www.9208361.org.cn/
http://news.9208361.org.cn/
http://www.9111316.cn/
http://news.9111316.cn/
http://www.bluelf.com.cn/
http://news.bluelf.com.cn/
http://www.qqq136com.cn/
http://news.qqq136com.cn/
http://www.2819w.cn/
http://news.2819w.cn/
http://www.9019758.org.cn/
http://news.9019758.org.cn/
http://www.wydaogou.cn/
http://news.wydaogou.cn/
http://www.ralhys.cn/
http://news.ralhys.cn/

相關推薦

JS 資料型別拷貝拷貝

js 資料型別六種 基本資料型別:Boolean. 布林值,true 和 false.null. 一個表明 null 值的特殊關鍵字。 JavaScript 是大小寫敏感的,因此 null 與 Null、NULL或其他變數完全不同。undefined. 變數未定義時的屬性。N

引用型別拷貝拷貝的區別

ICloneable 介面:支援克隆,即用與現有例項相同的值建立類的新例項。 MemberwiseClone 方法:建立當前 System.Object 的淺表副本。 淺拷貝:給物件拷貝一份新的物件。 淺拷貝的定義 —— 只對值型別(或string)型別分配新的記憶

js物件的直接拷貝拷貝

  最近Vue專案中寫到一個業務,就是需要把對話方塊的表單中的資料,每次點選提交之後,就存進一個el-table表格中,待多次需要的表單資料都提交進表格之後,再將這個表格提交,實現多個表單資料的同時提交,期間還可以用表格進行預覽、修改等其他操作。將每個表單資料存進表格的程式碼大致程式碼如下:     let&

Python——拷貝拷貝

賦值 我們先定義一個變數a,然後指向數值為100的這個空間,然後建立的變數b和a指向了同一個空間地址。 物件之間的賦值本質就是引用的傳遞。 那麼思考一下,在針對操作可變變數資料的時候,修改了a的值之後b的變化是否會發生變化? 答案自然會是肯定的,因為賦值的本質只是引用的傳遞,只要傳

python直接拷貝拷貝

python中,物件賦值實際上是物件的引用。當建立一個物件,將其賦值給另一個變數,python並沒有拷貝這個物件,而是拷貝了這個物件的引用。 所以如果從單純的賦值語句來實現clone物件的話, 那可能bug出現的也會莫名其妙. Python中可以使用copy模組來複制物件.

python的拷貝拷貝三者之間的區別

python的賦值、淺拷貝和深拷貝三者之間的區別 類似的文章已經很多了,但是在學習過程中,還是有一些模糊的地方,所以這裡把自己的理解記錄下來,便於以後複習,溫故知新! 1.賦值 賦值是將一個物件的地址賦值給一個變數,讓變數指向該地址( 舊瓶裝舊酒 )。 修改不可

事件捕獲冒泡綁定委托兼容滾輪

his navi dev apt 屬性 事件冒泡 rac abc delta clientX/Y 可視區的鼠標坐標 全兼容 offsetX/Y 鼠標坐標到物體邊框的距離 IE+Chrome pa

char布爾算數運算符

width 算數運算 png span ont http .com true bubuko Char字符: boolean類型: 布爾值只有true、false值,不像C語言有0、1值   布爾值可以直接判斷 賦值運算符: 算數運算符: char、布爾、賦

7中置一元結合applyupdateunapply提取器

pan print bsp code new collect str 賦值語句 定義 中置操作符 scala> 1 to 5 res0: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5

C++ protobuf 自定義資料型別

對於C++ protobuf 自定義資料型別的賦值,有兩種方式 set_allocate_XXX mutable_XXX 舉例說明 message SAT_JSON_CONFIG { required int32 AxisYMax = 1; required i

【plugins】Summernote的引入初始化方法詳解

一、引入 Summernote是一個簡單靈活的所見即所得的 HTML 線上編輯器,基於 jQuery 和 Bootstrap 構建,支援快捷鍵操作,提供大量可定製的選項。 <link href="https://cdn.bootcss.com/bootstrap/3.3.4

算數比較邏輯運算子

算數運算子 加減乘除模(取餘) 運算子: + - / %(取餘數) 保持原樣性 int x=2810; System.out.println(x/1000);//結果為2,必須保證int的原樣性 模得小口訣:左邊小於右邊取餘,左邊小

javascript運算子——條件逗號()void運算子

前面的話   javascript中運算子總共有46個,除了前面已經介紹過的算術運算子、關係運算符、位運算子、邏輯運算子之外,還有很多運算子。本文將介紹條件運算子、逗號運算子、賦值運算子、()和void運算子 條件運算子   條件運算子是javascript中唯一的一個三元運算子(三個運算元),有時

Python中的列表(新增二元列表連線查詢排序反序刪除分片操作負索引

#_*_coding:UTF-8_*_ # 列表list_name=[element1,element2,element3...] # 1.新增元素 # append(value)使用者在List的尾部新增一個元素 # insert(index,value)第一個引數ind

JAVA實驗二:對陣列進行初始化按形式列印

題目:按照要求使用Java編碼。 (1)以型別int[][]宣告一個叫matrix的二維陣列變數,將矩陣初始化為一個5個元素的陣列。 (2)以下列方式為matrix的內部元素賦值:matrix從零開始迴圈到其長度值;例如索引為i,在每次迭代中,將matrix[i]指向一個新的整數

html中radio的獲取註冊事件。

1,radio分組 只要name一樣,就是一組的,即一組中只能選擇一個,如下: <span>group1:</span> <input type="radio" id="radio1" checked="checked" name="grou

C++順序容器類中物件初始化swap

順序容器中,對於初始化物件,除了使用列表和一對迭代器初始化物件時,列表中元素和求迭代器所引用的值跟初始化的物件相容即可。其他的都需要保證型別完全相同 至於賦值,C++順序容器來還定義了一個assig

java運算子-算數比較

1.算術運算子 運算子是用來計算資料的符號。資料可以是常量,也可以是變數。被運算子操作的數我們稱為運算元。 運算子 運算規則 範例 結果 + 正號 +3

python元組的建立更新刪除等操作例項原始碼

#coding=utf8 ''' 元組是跟列表非常相近的另一種容器型別。 元組是一種不可變型別,一旦建立不可以修改其中元素。 由於這種特性,元組能做一個字典的key。 當處理一組物件時,這個組預設是元

讓我們一步一步實現一個完整的 String 類:構造拷貝移動析構

一、引言 我們在面試 C++ 相關崗位的時候,總會遇到這樣的筆試面試題: 請你實現一個 String 類 這道題,說簡單也簡單,說難也難,是一個考察 C++ 基礎的非常好的題目。正好在今天,我萌生了一個想法,那就是一步一步,一點一點,從構造析構,到