JS 資料型別、賦值、深拷貝和淺拷貝
js 資料型別
- 六種 基本資料型別:
- Boolean. 布林值,true 和 false.
- null. 一個表明 null 值的特殊關鍵字。 JavaScript 是大小寫敏感的,因此 null 與 Null、NULL或其他變數完全不同。
- undefined. 變數未定義時的屬性。
- Number. 表示數字,例如: 42 或者 3.14159。
- String. 表示字串,例如:"Howdy"
- Symbol ( 在 ECMAScript 6 中新新增的型別).。一種資料型別,它的例項是唯一且不可改變的。
- 以及 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、中置、一元、賦值、結合、apply和update、unapply提取器
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++ 基礎的非常好的題目。正好在今天,我萌生了一個想法,那就是一步一步,一點一點,從構造析構,到