你不知道的es5的面向對象

前言:

本文只爲本身最近看了不少關於es5對象方面的視頻以及書籍,深感前端之路路漫漫其修遠兮,才寫了此篇博客,也是鄙人第一次寫博客,寫的很差你們勿怪。另外本文總結的是es5方面的知識點,就不會用到什麼let,const以及class之類的es6語法,爲了與標題保持一致。我也會在以後總結es6以及typescript方面的知識。還有本文的全部代碼都是親測運行有效後才複製上來的,基本不會存在報錯的狀況,若是有錯誤或者代碼哪兒有問題還望各位大佬多多指教,畢竟我也只是正在前端學習的路上而已,也不是什麼大牛。代碼並不是是書上的源碼,我寫的比較簡單,緣由是照顧一些初學者。若是你在做者的文章中學到了一些新知識不仿給做者點個贊吧,在這裏先謝謝了。javascript

對象的定義:

無序屬性的集合(簡單易懂一點,不喜歡扯太複雜,也不喜歡拖泥帶水)html

重要概念:

  1. 私有變量:對於對象而言,全部的屬性都是公有的,嚴格來說是沒有私有屬性的說法的,但變量是有私有的。
  2. 私有方法:在構造函數內部不能爲外界訪問,只能經過函數內部方法訪問的方法。
  3. 公有屬性:在構造函數內部,能夠爲外界直接訪問的屬性。
  4. 公有方法:在構造函數內部,能夠爲外界直接訪問的方法。
  5. 特權方法:能夠訪問構造函數中私有屬性的方法。
  6. 靜態屬性和靜態方法:無需實例化就能夠調用的屬性和方法就叫靜態屬性和靜態方法。
  7. 實例:程序使用類建立對象時,生成的對象叫做類的實例
  8. 實例化:由類建立對象實例的過程叫作實例化
  9. 原型鏈:當訪問某個對象的屬性時,若是該對象沒有此屬性,則會到他的原型中去查找,它的原型屬性中又有本身的原型屬性,一直這樣查找下去,這就構成了原型鏈(僅表明我的理解,並未找到參考資料)
  10. 原型:對象中固有的__proto__屬性,該屬性指向對象的prototype原型屬性。
  11. this指向:正常來說this指向其執行上下文環境,但this的指向也不是固定不變的,後面會深刻討論this的指向問題。
  12. 閉包:是可訪問上一層函數做用域裏變量的函數,即使上一層函數已經關閉。
  13. 遞歸:遞歸最簡單的解釋就是本身調用本身
  14. 做用域:window中聲明的變量是全局變量,全局變量處在全局做用域中,函數中聲明的變量是局部變量,局部變量在函數做用域中。(另外在es6用let和const在代碼塊中聲明的變量具備塊級做用域)
  15. 做用域鏈:函數訪問某一個變量值時,此函數會在該函數中尋找該變量,若是沒有則到函數外尋找,最終會到window中尋找,這就造成了做用域鏈。
  16. 淺拷貝:只複製一層對象屬性,只是將數據中存放的引用拷貝下來,但依舊指向同一個存放地址。
  17. 深拷貝:則遞歸複製了全部層級,將數據中全部的數據都拷貝下來,而不只僅是引用。拷貝下來的數據的修改,並不會影響原數據。

對象的聲明方式:

  1. 字面量方式聲明:
    優缺點:簡單快捷,使用一個接口建立對象會產生大量重複代碼
var obj = {};
複製代碼
  1. 構造函數方式聲明:
    優缺點:能夠標識對象的類型,但每一個方法都會在實例上從新建立一遍
var Obj = function(name,age,sex ){
		this.name = name;
		this.age = age;
		this.sex = sex;
	}
	var newObj = new Obj('張三',24,'男');
	console.log(newObj);
	console.log(newObj.constructor == Obj);//constructor構造器
	console.log(newObj instanceof Object);//instanceof判斷是否爲該對象的子類
	console.log(newObj instanceof Obj);
	var o = new Object;
	Obj.call(o,'李四',26,'女');
	console.log(o.name);
複製代碼
  1. 工廠模式聲明:
    優缺點:能夠建立多個類似對象,但未解決對象類型的識別問題
var obj = function(name,age,sex){
		var o = new Object() || {};//new寫煩了,試了一下{}建立,也沒有問題。
		o.name = name;
		o.age = age;
		o.sex = sex;
		return o;
	}
	var newObj = obj('張三',24,'男');
	console.log(newObj);
複製代碼
  1. 原型模式聲明:
    優缺點:它的優勢不只能自定義對象類型,更重要的是能夠在全部引用類型(Object、Array、String)中建立新方法,雖然也能夠修改,但並不推薦。原型中不少實例被共享,對於引用類型值來講問題就比較突出。
var Obj = function(){};
	Obj.prototype.name = '張三';
	Obj.prototype.age = 24;
	Obj.prototype.sex = '男';
	var o = new Obj;
	console.log(o.name);
	console.log(Obj.prototype.isPrototypeOf(o));//判斷對象的原型prototype是否指向Obj;
	console.log(Object.getPrototypeOf(o)==Obj.prototype);//同Obj.prototype
	console.log(Obj.hasOwnProperty('name'));//在該對象實例中是否有該屬性,有就返回true
	console.log('name' in Obj);//在該對象中是否有該屬性,不論是原型上仍是實例上,有就返回true
	console.log(o.hasOwnProperty('name'));
	console.log('name' in o);
複製代碼

更簡單的方式:前端

var Obj = function(){};
	Obj.prototype={
	    name : '張三',
	    age : 24,
	    sex : '男'
	}
複製代碼
  1. 動態原型模式:
    優缺點:集合了構造函數和原型的優勢,但不能使用對象字面量重寫原型
var Obj = function(name,age,sex){
		this.name = name;
		this.age = age;
		this.sex = sex;
		if(typeof this.sayName != 'function'){
			Obj.prototype.sayName=function(){
				console.log(this.name);
			}
		}
	}
	var o = new Obj('張三',24,'男');
	o.sayName();
複製代碼
  1. 寄生構造函數模式:
    優缺點:優勢太過奇特了,有點兒說不上來,instanceof檢測不到其類型
var Obj = function(){
		var values = new Array(); 
		values.push.apply(values, arguments);
		values.toPipedString = function(){
 			return this.join("|");
 		}; 
 		return values; 
	}
	var o = new Obj("red", "blue", "green"); 
	console.log(o.toPipedString());
複製代碼
  1. 穩妥的構造函數模式:
    優缺點:沒有任何除了定義的get方法訪問的到其屬性,具備必定的安全性,與構造函數之間沒有太大的關係
var Obj = function(name,age,sex){
		var o = new Object();
		o.getName = function(){
			console.log(name);
		}
		return o;
	}
	var newObj = Obj('張三',24,'男');
	newObj.getName();
複製代碼
  1. 混合模式:
    注:以構造函數和原型模式組合爲例。
    優缺點:能夠結合其餘模式的優勢,缺點目前沒發現
var Obj = function(name,sex,age){
		this.name = name;
		this.sex = sex;
		this.age = age;
	}
	Obj.prototype = {
		getName:function(){
			console.log(this.name);
		}
	}
	var o = new Obj('李四',24,'男');
	o.getName();//李四
複製代碼

對象的特性:

前言:衆所周知對象有三個特性,也是比較重要的特性——封裝、繼承、多態。java

封裝:封裝就是有些私有的屬性和方法,用戶只能經過公有方法去訪問這些私有屬性。

注:文檔開頭已註明了基本概念es6

var Obj = function(name){
		var name = name;//私有變量
		this.age = 26;//公有屬性
		this.getName = function(){//特權方法
			return name;
		};
		Introduce = ()=>{//私有方法
			console.log(this);
			//若是不用es6的箭頭函數,那麼this指向的是window,this.age始終未定義,es6箭頭函數指向它所在的父級做用域,this.age就會獲取到26的值(實在不得已破了個例)
			return name + '今年' + this.age + '歲了';
		};
		this.getIntroduce = function(){//公有方法
			return Introduce;
		}
	}
	var o = new Obj('張三');
	console.log(o);//Obj{age: 26, getName: ƒ}
	console.log(o.name);//undefined
	console.log(o.getName());//張三
	// console.log(o.Introduce());//會報錯
	var newGetIntroduce = o.getIntroduce();
	console.log(newGetIntroduce());
複製代碼

多態:多態就是構造函數根據傳入的變量不一樣,對同一方法,有不一樣的返回結果

var Obj = function(name,age,sex){
		this.name = name;
		this.age = age;
		this.sex = sex;
	}
	var newObj1 = new Obj('張三',24,'男');
	var newObj2 = new Obj('李四',18,'女');
	console.log(newObj1);
	console.log(newObj2);//多態指得就是調用同一個函數產生不一樣對象的這種特性
複製代碼

繼承:一個對象繼承另外一個對象的屬性以及方法(最重要)

  1. 原型鏈方式繼承:
var Obj = function(){
    var name = '張三';
    this.getName = function(){
        return name;
    };
};
var newObj = function(){};
newObj.prototype = new Obj();//實現繼承

var O = new newObj();
console.log(O.getName());
複製代碼
  1. 構造函數方式繼承:
var Obj = function(name){
    this.name = name;
};
var newObj = function(){
    Obj.call(this,'張三');
};
var O = new newObj();
console.log(O.name);//張三
複製代碼
  1. 組合方式繼承:(原型鏈+call)
var Obj = function(){
    this.name = '張三';
}
Obj.prototype.getFn = function(){
   return this.name;
}
var newObj = function(){
    Obj.call(this);//繼承Obj函數內部this上的值
}
newObj.prototype = new Obj();//繼承Obj函數原型鏈上的值
var o = new newObj();
console.log(o.getFn());//張三
複製代碼
  1. 寄生繼承:
var newObj = function(Obj){
    var o = Object.create(Obj);//實現繼承
    o.getName = function(){
        return o.name;
    }
    return o;
}
var Obj = {
    name:'張三'
}
var Obj1 = newObj(Obj);
console.log(Obj1.getName());//張三
複製代碼
  1. 寄生組合式繼承:
var Obj = function(){
    this.name = '張三';
}
Obj.prototype.getName = function(){
    return this.name;
}
var newObj = function(){
    Obj.call(this);//獲取對象中的自身屬性值
}
var inherit = function(Obj,newObj){
    var prototype = Object.create(newObj.prototype);//獲取子類的原型
    prototype.constructor = Obj;//將父對象放置在子對象的constructor下
    Obj.prototype = prototype;//將父對象的原型賦值給子對象的原型
}
inherit(newObj,Obj);
var O = new newObj();
console.log(O.getName());//張三
複製代碼
  1. call()/apply()方法繼承:
//call()
var Obj = function(){
    this.name = '張三';
}
var newObj = function(){
    Obj.call(this);//實現繼承
    this.getName = function(){
        return this.name;
    }
}
var o = new newObj();
console.log(o.getName());//張三
//apply()
var Obj = function(){
    this.name = '張三';
}
var newObj = function(){
    Obj.apply(this);//實現繼承
    this.getName = function(){
        return this.name;
    }
}
var o = new newObj();
console.log(o.getName());//張三
複製代碼
  1. 對象冒充:
var Obj = function(){
    this.name = '張三';
}
var newObj = function(){
    this.Obj = Obj;
    this.Obj();//實現繼承
    delete this.Obj;
    this.getName = function(){
        return this.name;
    }
}
var o = new newObj();
console.log(o.getName());
複製代碼
  1. 原型式繼承:
var inherit = function(o){//實現繼承的函數
    function F(){};
    F.prototype = o;
    return new F();
}
var Obj = {
    name:'張三'
}
var newObj = inherit(Obj);
console.log(newObj.name);//張三
複製代碼

this指向問題:

  1. 在js文件中直接打印this,打印結果爲window:
console.log(this);//window
複製代碼
  1. 在函數中打印this,打印結果爲Window:
function fn(){
        console.log(this);//Window
    };
    fn();
複製代碼
  1. 在對象中打印this,this的指向爲該對象:
var Obj = {
		_this : function(){
			return this;
		}
    }
	console.log(Obj._this());//Obj
複製代碼
  1. 若是是綁定的dom事件,該事件方法中的this指向該dom元素:
//html部分
 <div id="v1" style="width: 200px; height: 200px;background-color: #666;position: absolute;top:50%;left: 50%;transform:translate(-50%,-50%);"></div>
 //js部分
 var v1 = document.getElementById('v1');
 v1.onclick = function(){
    console.log(this);
 }//返回結果爲html部分所有內容
複製代碼

小結:綜上所述,this通常狀況下指向其執行的上下文環境(es6箭頭函數中this指向其父級做用域)算法

那麼如何改變this的指向呢?

  1. 經過call方法來改變其this指向:
function fn(){
    console.log(this);
};
var Obj = {
    name:'李四',
    getThis:function(){
        console.log(this);
    }
}
fn();//Window {parent: Window, opener: null, top: Window, length: 0, frames: Window, …}
fn.call(Obj);//{name: "李四", getThis: ƒ}
複製代碼
  1. 經過apply方法來改變其this指向:
function fn(){
    console.log(this);
};
var Obj = {
    name:'李四',
    getThis:function(){
        console.log(this);
    }
}
fn();//Window {parent: Window, opener: null, top: Window, length: 0, frames: Window, …}
fn.apply(Obj);//{name: "李四", getThis: ƒ}
複製代碼
  1. 經過bind方法來改變其this指向:
function fn(){
    console.log(this);
};
var Obj = {
    name:'李四',
    getThis:function(){
        console.log(this);
    }
}
fn();//Window {parent: Window, opener: null, top: Window, length: 0, frames: Window, …}
fn.bind(Obj)();//{name: "李四", getThis: ƒ}
複製代碼

做用域和做用域鏈:

做用域:在es5中有全局做用域和函數做用域。
全局做用域:在函數做用域中,能訪問到全局做用域中的變量typescript

var num = 1;
    function fn(){
        console.log(num);//1
    }
    fn();
複製代碼

函數做用域:在函數中的變量,在函數外部的全局做用域中是訪問不到的json

function fn(){
    	var num = 1;
    	console.log(num);//1
	}
	fn();
	console.log(num);//報錯:num is not defined
複製代碼

一種比較特殊的狀況:若變量沒有用關鍵字進行聲明,那麼默認是全局變量。數組

function fn(){
		num = 1;
	}
	fn();
	console.log(num);//1
	//另外說一下,這種寫法在嚴格模式下會報錯。
複製代碼

這種寫法雖然能夠能在全局做用域中拿到函數中的變量,但通常不推薦這麼寫。
在es5中沒有塊級做用域,然而在許多狀況下會產生必定的問題:瀏覽器

for(var i=0;i<2;i++){
    console.log(i);//0,1;
}
console.log(i);//2
複製代碼

在尚未let和const的塊級做用域時,咱們的前輩已經想到了用私有做用域來模仿塊級做用域:

(function(){
    for(var i=0;i<2;i++){
        console.log(i);//0,1;
    }
})();
console.log(i);//i is not defined
複製代碼

這樣外界在for循環結束後就訪問不到i變量了。
小結:若是要經過一個標準的方法訪問函數中的變量的話,推薦使用閉包。

閉包:

  1. 基本概念:是可訪問上一層函數做用域裏變量的函數,即使上一層函數已經關閉。(我的理解:能夠訪問函數中局部變量的函數叫作閉包)
  2. 使用場景:當咱們須要在模塊中定義一些變量,並但願這些變量一直保存在內存中但又不會 「污染」 全局的變量時,就能夠用閉包來定義這個模塊。
  3. 代碼示例:
function fn(){
        var name = '張三';
        var age = 20;
        return function(){
            return name + '今年' + age + '歲了';
        }
    }
    var newFn = fn();
    console.log(newFn());//張三今年20歲了
複製代碼
  1. 閉包的優缺點:
    優勢:1:變量長期駐紮在內存中;2:避免全局變量的污染;3:私有成員的存在 ;
    缺點:1.致使內存泄漏;2.會改變父函數內部變量的值
    產生內存泄漏的緣由: 若是一個對象再也不被引用,那麼這個對象就會被 GC回收,不然這個對象一直會保存在內存中(兩個對象相互引用也會被回收,另外提一句谷歌v8引擎下是分代式回收機制:新生代和老生代)
  2. 解決閉包內存泄漏的方法:在退出函數以前,將不使用的局部變量所有刪除。
  3. 垃圾回收策略:標記清除(較爲經常使用)和引用計數。

遞歸:

  1. 基本概念:函數調用自身的函數名形成的
  2. 代碼示例:
function fn(n){
		if(n <= 1){
			return 1;
		}else{
			return n*fn(n-1);
		}
	}
	console.log(fn(10));//3628800
複製代碼

3.遞歸的優缺點:
優勢:在樹的前序,中序,後序遍歷算法中,遞歸的實現明顯要比循環簡單得多。
缺點:

  1. 遞歸因爲是函數調用自身,而函數調用是有時間和空間的消耗的:每一次函數調用,都須要在內存棧中分配空間以保存參數、返回地址以及臨時變量,而往棧中壓入數據和彈出數據都須要時間。->效率
  2. 遞歸中不少計算都是重複的,因爲其本質是把一個問題分解成兩個或者多個小問題,多個小問題存在相互重疊的部分,則存在重複計算,如fibonacci斐波那契數列的遞歸實現。->效率
  3. 調用棧可能會溢出,其實每一次函數調用會在內存棧中分配空間,而每一個進程的棧的容量是有限的,當調用的層次太多時,就會超出棧的容量,從而致使棧溢出。->性能

深淺拷貝:

  1. 淺拷貝:
    基本概念:只複製一層對象屬性,只是將數據中存放的引用拷貝下來,但依舊指向同一個存放地址。
    實現方式:
    (1). Object.assign():
let a = {
      age: 1
    }
    let b = Object.assign({}, a)
    a.age = 2
    console.log(b.age)//1
複製代碼

(2). 經過展開運算符 ... 來實現淺拷貝:

let a = {
      age: 1
    }
    let b = { ...a }
    a.age = 2
    console.log(b.age) // 1
複製代碼
  1. 深拷貝:
    基本概念:則遞歸複製了全部層級,將數據中全部的數據都拷貝下來,而不只僅是引用。拷貝下來。
    實現方式:
    JSON.parse(JSON.stringify(object)):(不止這一個,不過其餘的不太好記,暫時只寫這一個了)
let a = {
    age: 1,
    jobs: {
        first: 'FE'
    }
}
let b = JSON.parse(JSON.stringify(a))
a.jobs.first = 'native'
console.log(b.jobs.first) // FE
複製代碼

缺點:會忽略 undefined、不能序列化函數、不能解決循環引用的對象(會忽略es6的symbol)

皮一下:你覺得這就完了?不這纔剛剛開始,接下來將一一列舉Object的方法以及屬性!(可能有的能容會與以前的內容有所重複)

Object的屬性與方法列舉:(力求作到簡單易懂)

前言:若是有沒有列舉到的屬性以及方法歡迎補充
先看一下基本目錄吧:
(這是官方解釋,後面我會根據本身的理解給你們簡單的解釋一下)

屬性:

  1. Object.prototype:屬性表示 Object 的原型對象
  2. Object.prototype.constructor:全部對象都會從它的原型上繼承一個 constructor 屬性

方法:

  1. Object.assign():用於將全部可枚舉屬性的值從一個或多個源對象複製到目標對象。
  2. Object.create():建立一個新對象,使用現有的對象來提供新建立的對象的__proto__。
  3. Object.defineProperties():直接在一個對象上定義新的屬性或修改現有屬性,並返回該對象。
  4. Object.defineProperty():會直接在一個對象上定義一個新屬性,或者修改一個對象的現有屬性, 並返回這個對象。
  5. Object.entries():返回一個給定對象自身可枚舉屬性的鍵值對數組,其排列與使用 for...in 循環遍歷該對象時返回的順序一致(區別在於 for-in 循環還會枚舉原型鏈中的屬性)。
  6. Object.values()方法返回一個給定對象自身的全部可枚舉屬性值的數組,值的順序與使用for...in循環的順序相同 ( 區別在於 for-in 循環枚舉原型鏈中的屬性 )。
  7. Object.keys() 方法會返回一個由一個給定對象的自身可枚舉屬性組成的數組,數組中屬性名的排列順序和使用 for...in 循環遍歷該對象時返回的順序一致 。
  8. Object.freeze():能夠凍結一個對象。
  9. Object.fromEntries() 方法把鍵值對列表轉換爲一個對象。
  10. Object.getOwnPropertyDescriptor() 方法返回指定對象上一個自有屬性對應的屬性描述符。(自有屬性指的是直接賦予該對象的屬性,不須要從原型鏈上進行查找的屬性)
  11. Object.getOwnPropertyDescriptors() 方法用來獲取一個對象的全部自身屬性的描述符。
  12. Object.getOwnPropertyNames()方法返回一個由指定對象的全部自身屬性的屬性名(包括不可枚舉屬性但不包括Symbol值做爲名稱的屬性)組成的數組。
  13. Object.getOwnPropertySymbols() 方法返回一個給定對象自身的全部 Symbol 屬性的數組。
  14. Object.getPrototypeOf() 方法返回指定對象的原型(內部[[Prototype]]屬性的值)。
  15. Object.is() 方法判斷兩個值是不是相同的值。
  16. Object.isExtensible() 方法判斷一個對象是不是可擴展的(是否能夠在它上面添加新的屬性)。
  17. Object.isFrozen()方法判斷一個對象是否被凍結。
  18. Object.isSealed() 方法判斷一個對象是否被密封。
  19. Object.preventExtensions()方法讓一個對象變的不可擴展,也就是永遠不能再添加新的屬性
  20. hasOwnProperty() 方法會返回一個布爾值,指示對象自身屬性中是否具備指定的屬性(也就是,是否有指定的鍵)。
  21. isPrototypeOf() 方法用於測試一個對象是否存在於另外一個對象的原型鏈上。
  22. propertyIsEnumerable() 方法返回一個布爾值,表示指定的屬性是否可枚舉。
  23. toLocaleString() 方法返回一個該對象的字符串表示。此方法被用於派生對象爲了特定語言環境的目的(locale-specific purposes)而重載使用。
  24. toString() 方法返回一個表示該對象的字符串。
  25. valueOf() 方法返回指定對象的原始值。
  26. Object.seal()方法封閉一個對象,阻止添加新屬性並將全部現有屬性標記爲不可配置。當前屬性的值只要原來是可寫的就能夠改變。

具體解析:

  1. Object.prototype:能夠在已知對象上添加相應的屬性以及方法,甚至是修改對象原型上的的方法。可是並不推薦修改對象原型上的方法,由於這樣作有可能會致使不可預測的錯誤。
var Obj = function(name){
		this.name = name;
		this.isShow = true;
	};
	Obj.prototype.age = 23;
	Obj.prototype.getName = function(){
		if(this.isShow){
			console.log('個人名字是' + this.name + ',今年' + this.age + '歲');
		}
	}
	var newObj = new Obj('張三');
	newObj.getName();//個人名字是張三,今年23歲
複製代碼
  1. Object.prototype.constructor:全部對象都會從它的原型上繼承一個 constructor 屬性
var Obj = function(name){
    	this.name = name; 
    }
    var newObj = new Obj('張三');
    console.log(newObj.constructor);//ƒ (name){this.name = name; }
複製代碼
  1. Object.assign():這個方法絕大多數人都是用於淺拷貝,但這個方法的用處遠不止淺拷貝。由於用的比較多就詳細介紹一下吧。
    (1). 淺拷貝
var Obj = function(name){
	    this.name = name;
    }
    var newObj = new Obj('張三');
    var copy = Object.assign({},newObj);
    console.log(copy);//{name: "張三"}
複製代碼

也許你會好奇:這個爲何叫淺拷貝,跟深拷貝有什麼區別呢?那麼讓我用下面的例子爲你解釋一下吧!

var Obj = function(name){
	    this.name = name;
	    var age = 24;
	    this.getAge = function(){
		    return age;
	    }
    }
    Obj.prototype.getName = function(){
	    return this.name;
    }
    var newObj = new Obj('張三');
    var copy = Object.assign({},newObj);
    console.log(newObj);//{name: "張三", getAge: ƒ},你點開__proto__是能找到getName的方法的
    console.log(copy);//{name: "張三", getAge: ƒ}你點開__proto__是找不到getName的方法的
複製代碼

小結:淺拷貝只是將對象中的屬性以及方法進行拷貝,而對象原型上的屬性以及方法是沒有進行任何拷貝的。
(2). 合併對象:

var Name = {
    	name:'張三'
    }
    var Age = {
    	age:24
    }
    var Sex = {
    	sex:'男'
    }
    var newObj = Object.assign({},Name,Age,Sex);
    console.log(newObj);//{name: "張三", age: 24, sex: "男"}
複製代碼

(3). 合併具備相同屬性的對象:(後者覆蓋前者)

var Name = {
    	name:'張三'
    }
    var Age = {
    	name:'李四',
    	age:24
    }
    var Sex = {
    	name:'王五',
    	age:25,
    	sex:'男'
    }
    var newObj = Object.assign({},Name,Age,Sex);
    console.log(newObj);//{name: "王五", age: 25, sex: "男"}
複製代碼

(4). 拷貝訪問器:

var Obj = {
    	name:'張三',
    	get age(){
    		return 25
    	}
    }
    var copy = Object.assign({},Obj);
    console.log(copy);//{name: "張三", age: 25}
複製代碼
  1. Object.create():建立一個新對象,使用現有的對象來提供新建立的對象的__proto__。
var Obj = {
	    name:'張三',
	    isShow:false,
	    getName:function(){
	        if(this.isShow){
	           console.log('個人名字叫' + this.name);  
	        }
	    }
	}
	var newObj = Object.create(Obj);
	newObj.isShow = true;
	newObj.getName();//個人名字叫張三
複製代碼
  1. Object.defineProperties():直接在一個對象上定義新的屬性或修改現有屬性,並返回該對象。

語法:Object.defineProperties(obj, props)

其中props:
(1). configurable:可否經過delete刪除屬性從而從新定義屬性,可否修改屬性的特性,或者可否把屬性修改成訪問器屬性
(2). enumerable:表示可否經過for-in循環返回屬性
(3). value:與屬性關聯的值。能夠是任何有效的JavaScript值(數字,對象,函數等)。
(4). writable:表示可否修改屬性值
(5). get:獲取值。
(6). set:設置值。

var Obj = {sex:'男'};
	Object.defineProperties(Obj, {
	    'name':{
	       configurable:true,
	       enumerable:true,
	       value:'張三',
	       writable:true
	    },
	    Sex:{
	    	get:function(){
	    		return this.sex;
	    	},
	    	set:function(data){
	    		this.sex = data;
	    	}
	    }
	});
	Obj.Sex = '女';
	console.log(Obj.sex);//女
複製代碼
  1. Object.defineProperty:

對象中的數據屬性行爲特性:

注:修改如下4中數據屬性行爲的方法:Object.defineProperty
(1). configurable:可否經過delete刪除屬性從而從新定義屬性,可否修改屬性的特性,或者可否把屬性修改成訪問器屬性

var obj = {
		age:'24',
		sex:'男'
	};
	Object.defineProperty(obj,'name',{
		configurable:true,//規定該屬性值是否能被刪除,若是爲true則能被刪除,若是爲false則不能被刪除
		enumerable:false,
		//規定該屬性是否能夠for-in遍歷,true則能經過for-in打印出name的值,若是爲false則打印不出來name的值
		writable:true,//若是爲true,第二個name打印爲李四,若是爲false第二個name打印爲張三
		value:'張三'//設置name屬性的屬性值
	})
	delete obj.name;
	console.log(obj);
複製代碼

(2). enumerable:表示可否經過for-in循環返回屬性

var obj = {
		age:'24',
		sex:'男'
	};
	Object.defineProperty(obj,'name',{
		enumerable:false,
		//規定該屬性是否能夠for-in遍歷,true則能經過for-in打印出name的值,若是爲false則打印不出來name的值
		writable:true,//若是爲true,第二個name打印爲李四,若是爲false第二個name打印爲張三
		value:'張三'
	})
	for(var key in obj){
		console.log(obj[key]);
	}
	console.log(obj);
複製代碼

(3). writable:表示可否修改屬性值

var obj = {};
	Object.defineProperty(obj,'name',{
		writable:true,//若是爲true,第二個name打印爲李四,若是爲false第二個name打印爲張三
		value:'張三'
	})
	console.log(obj.name);
	obj.name = '李四';
	console.log(obj.name);
複製代碼

(4). value:包含這個屬性的數據值

var obj = {};
	Object.defineProperty(obj,'name',{
		value:'張三'
	})
	console.log(obj.name);//張三
複製代碼

訪問器屬性:

注:前兩種方法跟對象中的數據屬性行爲特性中的同樣,就很少贅述了
(1). configurable:規定該屬性值是否能被刪除
(2). enumerable:規定該屬性是否能夠for-in遍歷
(3). get:讀取屬性時調用的函數
(4). set:寫入屬性時調用的函數

var obj = {
		_name:'李四'
	};
	Object.defineProperty(obj,'name',{
		get:function() {
			return this._name;
		},
		set:function(newName){
			if(newName){
				this._name = newName;
			}
		}
	})
	obj.name = '';
	console.log(obj.name);//李四
複製代碼
  1. Object.entries():返回一個給定對象自身可枚舉屬性的鍵值對數組,其排列與使用 for...in 循環遍歷該對象時返回的順序一致(區別在於 for-in 循環還會枚舉原型鏈中的屬性)。
var Obj = {
	name:'張三',
	id:1
};
//for(var [key,value] of Object.entries(Obj)){
	//console.log(key + ',' + value);
//}//name,張三 id,1
console.log(Object.entries(Obj))//[[name,張三],[id,1]];
複製代碼

注:此處用for-in會打印出對象的key值爲0和1,然而value打印出來倒是未定義。

  1. Object.values()方法返回一個給定對象自身的全部可枚舉屬性值的數組,值的順序與使用for...in循環的順序相同 ( 區別在於 for-in 循環枚舉原型鏈中的屬性 )。
var Obj = {
	name:'張三',
	id:1
};
//for(var value of Object.values(Obj)){
	//console.log(value);
//}//張三 1
console.log(Object.values(Obj));//張三 1
複製代碼
  1. Object.keys() 方法會返回一個由一個給定對象的自身可枚舉屬性組成的數組,數組中屬性名的排列順序和使用 for...in 循環遍歷該對象時返回的順序一致 。
var Obj = {
		name:'張三',
		id:1
	};
	//for(var key of Object.keys(Obj)){
		//console.log(key);
	//}//name id
	console.log(Object.keys(Obj));//name id
複製代碼
  1. Object.freeze():能夠凍結一個對象。
var Obj = {
	    name:'張三',
	    id:1
	}
	Object.freeze(Obj);
	Obj.name = '李四';
	Obj.sex = '女';
	console.log(Obj);//{ name: "張三", id: 1 }
複製代碼

注:該方法可以讓對象變成只可讀不可寫的類型,能夠有效地防止別人在不須要修改的對象上添加屬性。
11. Object.fromEntries() 方法把鍵值對列表轉換爲一個對象。

var arr = [['name','張三'],['id',1]];
	console.log(Object.fromEntries(arr));//{ name: "張三", id: 1 }
複製代碼
  1. Object.getOwnPropertyDescriptor() 方法返回指定對象上一個自有屬性對應的屬性描述符。(自有屬性指的是直接賦予該對象的屬性,不須要從原型鏈上進行查找的屬性)
var Obj = {
    name:'張三'
};
console.log(Object.getOwnPropertyDescriptor(Obj,'name'));
//{ value: "張三", writable: true, enumerable: true, configurable: true }
複製代碼
  1. Object.getOwnPropertyDescriptors() 方法用來獲取一個對象的全部自身屬性的描述符。
var Obj = {
    name:'張三',
    id:1
}
console.log(Object.getOwnPropertyDescriptors(Obj));
//name: {value: "張三", writable: true, enumerable: true, configurable: true}
//id: {value: 1, writable: true, enumerable: true, configurable: true}
複製代碼
  1. Object.getOwnPropertyNames()方法返回一個由指定對象的全部自身屬性的屬性名(包括不可枚舉屬性但不包括Symbol值做爲名稱的屬性)組成的數組。
var Obj = {
    name:'張三',
    id:1
}
console.log(Object.getOwnPropertyNames(Obj));
複製代碼
  1. Object.getOwnPropertySymbols() 方法返回一個給定對象自身的全部 Symbol 屬性的數組。
var obj = {};
var a = Symbol("a");
var b = Symbol.for("b");

obj[a] = "akdfj";
obj[b] = "asdkflj";
var newObj = Object.getOwnPropertySymbols(obj);
console.log(newObj.length); // 2
console.log(newObj)         // [Symbol(a), Symbol(b)]
console.log(newObj[0])      // Symbol(a)
複製代碼
  1. Object.getPrototypeOf() 方法返回指定對象的原型(內部[[Prototype]]屬性的值)。
var Obj = {};
console.log(Object.getPrototypeOf(Obj));
/**{constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}**/
複製代碼
  1. Object.is() 方法判斷兩個值是不是相同的值。
console.log(Object.is([],[]));//false
console.log(Object.is({},{}));//false
console.log(Object.is(NaN,NaN));//true
console.log(Object.is(NaN,0/0));//true
console.log(Object.is("",false)); //false
複製代碼

注:只有左右兩側運行的值與其指向也徹底相同時纔會返回true

  1. Object.isExtensible() 方法判斷一個對象是不是可擴展的(是否能夠在它上面添加新的屬性)。
var Obj = {
	    name:'張三',
	    id:1
	}
	Object.freeze(Obj);
	console.log(Object.isExtensible(Obj));//false
複製代碼
  1. Object.isFrozen()方法判斷一個對象是否被凍結。
var Obj = {
	    name:'張三',
	    id:1
	}
	Object.freeze(Obj);
	console.log(Object.isFrozen(Obj));//true
複製代碼
  1. Object.isSealed() 方法判斷一個對象是否被密封。
    (1). 空對象變成密封對象:
var Obj = {};
Object.preventExtensions(Obj);//讓空對象變成不可擴展對象
console.log(Object.isSealed(Obj));//true;
複製代碼

(2).非空對象變成密封對象:

var Obj = {
    name:'張三',
}
Object.preventExtensions(Obj);//讓對象變成不可擴展對象
Object.defineProperty(Obj, "name", { configurable: false });//若是是非空對象必須將自身屬性變成不可配置才能成爲一個密封對象
console.log(Object.isSealed(Obj));
複製代碼
  1. Object.preventExtensions()方法讓一個對象變的不可擴展,也就是永遠不能再添加新的屬性。
var Obj = {
    name:'張三'
}
Object.preventExtensions(Obj);
console.log(Object.isExtensible(Obj));//false
複製代碼
  1. hasOwnProperty() 方法會返回一個布爾值,判斷對象是否有某個key值。
var Obj = {
    name:'張三'
}
console.log(Obj.hasOwnProperty('name'));//true
複製代碼
  1. isPrototypeOf() 方法用於測試一個對象是否存在於另外一個對象的原型鏈上。
function Bar() {}
function Baz() {}
Baz.prototype = Object.create(Bar.prototype);
var baz = new Baz();
console.log(Bar.prototype.isPrototypeOf(baz)); 
複製代碼
  1. propertyIsEnumerable() 方法返回一個布爾值,表示指定的屬性是否可枚舉。
var Obj = {
    name:'張三'
}
console.log(Obj.propertyIsEnumerable('name'));//true
複製代碼
  1. toLocaleString() 方法返回一個該對象的字符串表示。此方法被用於派生對象爲了特定語言環境的目的(locale-specific purposes)而重載使用。
var Obj = {
    name:'張三'
}
console.log(Obj.toLocaleString());//[object Object]
複製代碼

注:此方法的返回結果不太懂。

  1. toString() 方法返回一個表示該對象的字符串。
var Obj = {
    name:'張三'
}
console.log(Obj.toString());//[object Object]
複製代碼

注:JSON.stringify(Obj)可將對象轉化成json字符串

  1. valueOf() 方法返回指定對象的原始值。
//Number
var num = 1;
console.log(num.valueOf());//1
//String
var str = '我';
console.log(str.valueOf());//我
//Boolean
var bool = false;
console.log(bool.valueOf());//false
//以此類推
複製代碼
  1. Object.seal()將對象設置爲封閉對象:
var Obj = {
    name:'張三'
}
Object.seal(Obj);
console.log(Object.isSealed(Obj));//true
複製代碼

思考:

如何遍歷對象原型上的屬性及方法名?

var obj = function(){};
	obj.prototype.name = '張三';
	obj.prototype.id = 1;
	obj.prototype.getName = function(){
		return this.name;
	}
	var newObj = new obj;
	console.log(newObj);
	for(var key in newObj){
		console.log(key); 
	}//name,id.getName
複製代碼

如何實現數據的雙向綁定?

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>雙向綁定</title>
</head>
<body>
	<h1 id="title">hello</h1>
	<input type="text" id="inp">
	<script>
	var input = document.getElementById('inp');
	var title = document.getElementById('title');
	input.oninput = function(e){
		title.innerHTML = e.target.value || this.value;
	}
	//監聽input及title值的變化
	function monitor(Obj,Objkey){
		Object.defineProperty(Obj,Objkey,{
			get:function(){
				return input.value;
			},
			set(val){
				title.innerHTML = val;
				input.value = val;
			}
		})
	}
	monitor(title,'inner');
	title.inner = '123';
	// monitor(input,'val');
	// input.val = '456';
	</script>
</body>
</html>
複製代碼

如何實現對象轉數組?

var Obj = {
    name:'張三',
    id:1
}
function toArr(Obj){
    return Object.entries(Obj).flat();
}
console.log(toArr(Obj));//["name", "張三", "id", 1];
複製代碼

參考文獻:

  1. developer.mozilla.org/zh-CN/docs/…
  2. javascript高級程序設計(第三版)——尼古拉斯.澤卡斯

後記:

做者始終堅信實踐是檢驗真理的惟一標準,不論是報錯也好仍是這些零散的知識點也好,全部的代碼都只有你本身試過才知道有沒有效,沒有試過的代碼靠猜是猜不出來的,代碼的對錯瀏覽器會告訴你答案的!另外以上的知識點不少概念都是我本身理解了以後用本身的話表述的,若是有說錯的地方,還望各位大佬指點一二。

相關文章
相關標籤/搜索