JavaScript對象克隆
淺拷貝
淺拷貝引用值是要相互影響的,因爲它是棧內存之間的賦值,賦值的是地址,一個更改,其他的都要更改。
var obj = {
name :'abc',
age : 123,
sex : 'female',
card : ['visa','master']//存在一個引用值
}
var obj1 = {}
function clone(origin,target) {
var target = target || {};//防止沒有提前新建一個空對象
for(var prop in origin){
target[prop] = origin[prop];
}
}
clone(obj,obj1);
深度拷貝
深拷貝就是無論是原始值還是引用值,修改後彼此相互不影響。
遍歷對象 for(var prop in obj)(for in也可以遍歷數組(數組也是特殊類型的對象))
1,判斷是不是原始值 typeof() object—>引用值
2,判斷是數組還是對象 constructor,instanceof,toString
3,建立相應的數組或者對象
4,遞歸
// JavaScript Document
var obj = {
name :'abc',
age : 123,
sex : 'female',
card : ['visa','master']//存在一個引用值
}
var obj1 = {}
function deepClone(origin, target){
var target = target || {},
toStr = Object.prototype.toString,
arrStr = "[Object Array]";
for(var prop in origin){
if(origin.hasOwnProperty(prop)){
if(origin[prop] !== "null" && typeof(origin[prop]) == 'object'){
// if(toStr.call(origin[prop]) == arrStr){
// target[prop] = [];
// }else{
// target[prop] = {};
// }
//上面的ifelse條件判斷可以用下面的三目運算符代替:
target[prop] = toStr.call(origin[prop]) == arrStr ? [] : {};
deepClone(origin[prop],target[prop]);
}else{
target[prop] = origin[prop];
}
}
}
return target;
}
deepClone(obj,obj1);
更改引用值之間相互不影響
數組
數組的兩種創建方式
-
var arr = [];//字面量(內部還是new Array()的方式)
-
var arr = new Array();
改變原數組方法
- push()
功能:在數組的最後一位新增一個或多個數據,並且返回新數組的長度,會改變原來的數組
注意:push()方法返回的是數據是新數組的長度,它增加的數據可以是一個也可以是多個,可以理解爲增加一連串的數據
模擬系統調用數組的push方法:
Array.prototype.push = function(){
for(var i = 0; i <arguments.length; i++){
this[this.length] = arguments[i];
}
return this.length;
}
- pop()
功能:刪除數組的最後一位,並且返回刪除的數據,會改變原來的數組
注意:帶了參數也沒得用,還是刪除最後一個
- shift()
功能:刪除數組的第一位數據,並且返回新數組的長度,會改變原來的數組
帶不帶參數都是一樣的,只剪切數組的第一個元素
- unshift
在數組的第一位添加元素。返回的是數組的長度
- sort
數組直接調用sort()方法,它是 按照ASCII排序的。
ASCII比較大小的時候,是比較兩個數中的第一個字符。
- 常見ASCII碼的大小規則,0-9<A-Z<a-z:
- 數字比字母要小。如 “7”<“F”
- 數字0比數字9要小,並按0到9順序遞增。如 “3”<“8”
- 字母A比字母Z要小,並按A到Z順序遞增。如“A”<“Z”
- 同個字母的大寫字母比小寫字母要小32。如“A”<“a”
記住幾個常見字母的ASCII碼大小: “A”爲65;“a”爲97;“0”爲48;
讓數組按照大小排序就要實現sort高階函數的使用
1,必須寫兩個形參
2,看返回值
1)當返回值爲負數時,那麼前面的是放在前面
2)當返回值爲正數時,那麼後面的是放在前面
3)爲0,不動
var arr= [1,3,5,4,10];
arr.sort(function (a,b) {
if(a > b){
return 1;
}else{
return -1;
}
});
console.log(arr);//[1,3,4,5,10]
上面高階sort函數的實現可以簡化爲下面這種形式:
arr.sort(function (a,b) {
return a-b;//升序
// return b-a;//降序
});
它是這樣的執行過程,數組裏面的數首先傳a= 1,b = 3,1和3比,然後1,4。1,5。1,10 。3,4。3,5 。3,10 。4,5 。4,10 。5,10 。
這樣多次調用裏面的函數進行比較(就想冒泡排序一樣)
因爲 if(a > b){return 1;}就可以爲 if(a - b > 0){return 1;}—>return a - b
因爲這個時候a> b,只要返回整數就可以了
然後再else裏面就是a<b返回負數,那麼return a-b肯定是負數。所有它們都返回的是a-b,直接一下子就返回a - b
給一個有序的數組排序,讓它每一次執行都是亂序
var arr = [1,2,3,4,5,6,7];
arr.sort(function () {
return Math.random() - 0.5;
});
console.log(arr);
-
發現每執行一次,arr數組裏面的順序都不一樣。
-
因爲 Math.random()返回的結果是(0,1),這樣減去0.5,那麼它有爲正的概率和爲負的概率是一半一半。
-
根據sort函數的原理,如果返回的是正數,那麼後面的和前面的調個位置,如果返回的是負數或者0,那麼前面的還是在前面
將數組裏面的按字節長度排序
var arr = ['ac','nsfauigf','網絡','osy題詞','wdfewfef','addasd'];
function retBytes(str) {
var num = str.length;
for(var i = 0; i <str.length; i++){
if(str.charCodeAt(i) > 255){
num++;
}
}
return num;
}
arr.sort(function(a,b) {
return retBytes(a) - retBytes(b);
})
- reverse
數組反轉
- splice
splice ( 從第幾位開始截取 , 截取多少位 , 在截取點處添加什麼)
負數的實現原理是這樣的:pos += pos > 0 ? 0 : this.length;也就是倒數的第幾位
首先判斷,如果是負數的話那就是負數加上長度就等於正數的第幾位(從0開始)
不改變原數組方法
- concat
var arr = [1,2,3]
var arr1 = [4,5,6]
var arr3 = arr.concat(arr1);//arr3 : [ 1, 2, 3, 4, 5, 6 ]
例題:
把這一串字符串鏈接起來,不用+號的方式(因爲字符串是原始值,存在棧內存裏面的,這樣來回在棧內取元素很浪費時間。 )。
var str1 = "nsd";
var str2 = "sdsfd";
var str3 = "fadf";
var arr = [str1,str3];//數組是散列結構
console.log(arr.join(""));//nsdfadf
//console.log(arr.join());// nsd,fadf
join()裏面爲空就是用逗號連起來。
數組的方法join 和 字符串的方法split是互逆的
var arr= [1,2,3,4,5];
var str = arr.join("-"); // "1-2-3-4-5"
var shuzu =str.split("-");//[ "1", "2", "3", "4", "5" ]
- toString
var arr = [1,2,3,4]
console.log(arr.toString());//1,2,3,4
- slice
返回值是截取的值,不改變原數組。
-
slice兩個參數(從該位爲開始截取,截取到該位)
-
slice一個參數(從該位開始截取到後面所有都截取)
-
slice沒有參數(整個截取)
類數組
- 可以利用屬性名模擬數組的特性
- 可以動態的增加length屬性
- 如果強行讓類數組調用push方法,則會根據length屬性值的位置進行屬性值的擴充。
類數組舉例
1,arguments
2,一個對象:屬性要爲索引(數字)屬性,必須有length屬性,最好加上push
類數組裏面最關鍵的是length屬性,類數組中push實現的原理
Array.prototype.push = function (target) {
obj[obj.length] = target;
obj.length++;
}
var obj = {
"2" : "a",
"3" : "b",
"length" : 2,
"push" : Array.prototype.push
}
obj.push('c');
obj.push('d');
console.log(obj);
//Object { 2: "c", 3: "d", length: 4, push: push() }
var obj = {
"1" : "a",
"2" : "c",
"3" : "d",
"length" : 3,
"push" : Array.prototype.push
}
obj.push('b');
console.log(obj);
//Object { 1: "a", 2: "c", 3: "b", length: 4, push: push() }
var obj = {
"0" : 'a',
"1" : 'b',
"2" : 'c',
name :"abc",
age : 123,
length : 3,
push : Array.prototype.push,
splice :Array.prototype.splice
}
類數組的好處就是把數組和對象的好處拼到一起,
但是並不是所有的數組方法都能用,除非你自己添加
封裝typeof方法
1,分兩類,原始值和引用值
2.區分引用值
function type (target){
var ret = typeof(target);
var template = {
"[object Array]" : "array",
"[object Object]" : "object",
"[object Number]" : "number-object",
"[object String]" : "string-object",
"[object Boolean]" : "boolean-object"
}
if(target === null){
return "null";
}else if(ret == 'object'){//引用值:數組,對象,包裝類
var str = Object.prototype.toString.call(target);
return template[str];//屬性值
}else{
return ret;//原始值:直接用typeof來區分
}
}
數組去重(要求在原型鏈上編程)
var arr = [1,1,1,1,2,2,2,2,2];
//利用對象的一個屬性只能對應一個值的特性來去重
//var obj = {
// "1" : 'abc',
// "2" : 'abc,
//}
"1"-->undefined
"1"-->"abc"
"2"-->undefined
"2"-->"abc"
//新建一個對象,遍歷數組裏面的每一個值拿來當做對象的一個屬性,如果該屬性已經存在了值,那就跳過,繼續找下一個值。
//看它是不是爲對象在對象裏面已經有了值,如果沒有值,那就把數組裏面的這個值作爲該對象的一個新的屬性。
//最後輸出對象的所有屬性就是去掉數組裏面重復的值。
Array.prototype.unique = function () {
var temp = {}; //新建一個空對象
var arr = []; //新建一個空數組,用來返回 去重後的結果
var len = this.length;
for(var i = 0; i < len; i++){
if (!temp[this[i]] ) { //this[i]表示arr[i]表示數組裏面的每一個值
//temp[this[i]]表示對象裏面的這個屬性(數組裏面的值)的值是不是undefined,如果是undefined,就說明這要添加到該對象裏面爲一個新的屬性。
//所以這個地方就是!temp[this[i]],表述如果它是!undefined == true就要給它賦值,然後push到去重數組裏面去。
temp[this[i]] = 'abc';
arr.push(this[i]);
}
}
return arr;
}
ES5嚴格模式
Javascript 的保留關鍵字不可以用作變量、標籤或者函數名。有些保留關鍵字是作爲 Javascript 以後擴展使用。ES5較ES3新添加了幾個保留關鍵字:
1)class:類,ES6引入
2)const:常量,ES6引入
3)enum:枚舉類型
4)extends:類繼承,ES6引入
5)import:模塊導入,ES6引入
6)export:模塊導出,ES6引入
7)super:調用父類的構造函數,ES6引入
瀏覽器兼容性
1)ES3,可以認爲所有瀏覽器都支持;
2)ES5,現代瀏覽器都支持(>=IE9), IE9不支持嚴格模式。
在try裏面的發生錯誤,不會執行錯誤後的try裏面的代碼
- try 測試代碼塊的錯誤。
- catch 語句處理錯誤。
- throw 創建並跑出錯誤。
try
{
//在這裏運行代碼
拋出錯誤
}
catch(err)
{
//在這裏處理錯誤
}
請輸出一個 5 到 10 之間的數字:
<input id="demo" type="text">
<button type="button" onclick="myFunction()">測試輸入</button>
<p id="mess"></p>
</body>
</html>
<script type="text/javascript">
function myFunction(){
try{
var x=document.getElementById("demo").value; 取元素的值
if(x=="") throw "值爲空"; 根據獲取的值,拋出錯誤
if(isNaN(x)) throw "不是數字";
if(x>10) throw "太大";
if(x<5) throw "太小";
}
catch(err){
var y=document.getElementById("mess"); 抓住上面throw拋出的錯誤,給p標籤顯示
y.innerHTML="錯誤:" + err + "。";
}
}
</script>
Error.name的六種值對應的信息
-
EvalError : eval()的使用與定義不一致
-
RangeError : 數組越界
-
ReferenceError : 非法或不能識別的引用數值(一般沒有定義就使用就會報ReferenceError)
-
SyntaxError :發生語法解析錯誤
-
TypeError : 操作數類型錯誤
-
URIError : URI處理函數使用不當
es5.0嚴格模式
啓動es5.0的嚴格模式用"use strict" ,放在邏輯的最頂端(全局嚴格模式),或者放在局部的最頂端讓它部分啓用es5.0的嚴格模式(局部函數內的嚴格模式)。
這個時候和es3.0產生衝突的部分全部使用es5.0的嚴格模式
"use strict"就是一行字符串。不會對不兼容嚴格模式的瀏覽器產生影響
不支持with,arguments.callee,function.caller
var obj={
name:"obj"
}
var name='window';
function test(){
var name="scope";
with(obj){
console.log(name);
}
}
test()
with它會改變作用域鏈,with(obj)裏面跟的obj會被放在作用域的最頂端,那麼首先查找作用域鏈的時候就會找obj裏面的。因爲with的功能太強大,消耗大量的資源。所以在嚴格模式裏面不準用
變量賦值前必須聲明。
var a = b = 3;//會報錯,b報錯
局部this必須被賦值(Person.call(null/undefined)賦值什麼就是什麼) ,就是說預編譯this不再指向window,它爲undefined,在全局範圍裏面this依舊是指向window。
es5拒絕重複的屬性 和參數
eval是魔鬼
eval 能該表作用域
數組去重
第一種方法用ES6裏面的Set特性來去重
var arr = [4,7,9,4,7,8,9,4];
var set = new Set();
for(var i = 0; i< arr.length; i++){
set.add(arr[i]);
}
console.log(set);
第二種方法用一個新的數組裏面indexOf屬型的性質來去重
(indexOf返回的就是字符出現在字符串的位置,或者判斷字符串出現在數組的位置,返回數組的下標)
var newArr=[];
for(var i = 0; i < arr.length; i++){
if(newArr.indexOf(arr[i]) == -1){
newArr.push(arr[i]);
}
}
console.log(newArr);
第三種方法就一句就可以實現去重
console.log(Array.from(new Set(arr)));
尋找落單
第一種方法:用ES6裏面的Set去重留下落單的那個。
var arr = [1,2,3,4,5,1,2,3,4];
var set = new Set();
for(var i = 0; i <arr.length; i++){
if(set.has(arr[i])){
set.delete(arr[i]);
}else{
set.add(arr[i]);
}
}
console.log(set);//結構是一個數組: Set(1) {5}
console.log(...set);//5 用展開運算符把數組展開
第二種方法:根據對象的屬性是不允許有重複的這一特性。
var arr = [1,2,3,4,5,1,2,3,4];
var obj = {};
for(var i = 0; i < arr.length; i++){
if(obj[arr[i]]){
delete obj[arr[i]];
}else{
obj[arr[i]] = true;
}
}
console.log(Object.keys(obj)[0]);//通過這種方法拿到對象的key
第三種方法用到了異或^
首先你要知道0和任何數異或都等於任何數,然後任何數和自己異或都爲0,
因爲相同爲0,不同爲1,它也存在交換律和結合律。
所以用它在找那個落單的數,是一定可以找到的。
var arr = [1,2,3,4,5,1,2,3,4];
var result = 0
for(var i = 0; i < arr.length; i++){
result ^=arr[i];
}
console.log(result);