javaScript基礎(對象篇)
javaScript中的面向對象編程分為以下幾層
- 第一層:單一對象的面向對象
- 第二層:對象間的原型鏈
- 第三層: 作為實例工廠的構造函數,類似于其他語言中的類
- 第四層:子類,通過繼承已有的構造函數,創建新的構造函數
JavaScript中的所以對象都是從字符串到值的映射。一個對象中的某一項(鍵、值)稱為屬性,屬性的鍵始終是文本字符串。一個對象中的某一項(鍵、值)稱為屬性。屬性的鍵始終是文本的字符串、屬性的值可以是任何JavaScript的值,包括函數。方法是值作為函數的屬性
對象字面量
--
JavaScript的對象字面量可以直接創建簡單對象(Object 的直接實例)
var jane={
name: 'Jane',
describe:function(){
return 'Person named' +this.name;
},
};
獲取屬性
jana.name//Jane
jane.describe//[Function]
刪除對象
delete 操作符允許你從一個對象中完全移除一個屬性(整個鍵值對),刪除只影響一個對象的直接(“自有的",非繼承的)屬性。這并不涉及它的原型,如果是自有屬性,不能被刪除,delete會返回false,其他所有情況都會返回true
var obj={hello:'world'};
delete obj.hello//true
obj.hello//undefined
var obj={}
Object.definedProperty(obj,'canBeDeleted',{
value:123,
configurable:true
## });
OBject.definedProperty(obj,'cannotBeDeleted',{
value:456,
configurable:false
});
delete obj.cannotBeDeleted//false
其他所有情況刪除屬性返回true
delete obj.doesNotExist//true
delete obj.canBeDeleted///true
特殊的鍵
可以使用var 和function作為屬性的鍵,也可以使用數字作為鍵,但是他們會被解析為字符串
var obj={var :'a',function:'b'}
obj.var//a
obj.function//b
var obj={0.7:'abc'}
Object.key(obj)//['0.7']
obj['0.7']//'abc'
任意值轉對象
值 | 結構 |
---|---|
無參數調用 | {} |
undefined | {} |
null | {} |
布爾值 | new Boolean(bool) |
數字 | new Number(num) |
字符串 | new String(Str) |
對象 | new Object(obj) |
Object(null) instance Object//true
Object(false) instanceOf Boolean
var obj ={}
Object(obj)===obj//true
this作為函數和方法的隱式參數
當你調用一個函數時,this總是作為一個隱式參數
- 寬松模式中的普通函數, 盡管普通函數中的this沒有實際用處,但它仍然作為一個特殊的變量存在,它的值總是指向全局對象
function returnThisSloopy(){return this}
returnThissloppy()===window//true
- 嚴格模式下的普通函數
this 總是undefined
function returnThisStrict(){
'use strict'; ff
return this
}
returnThisStrict()===undefined//true
- 方法,this指向調用方法的對象
var obj={method:returnthisStrict};
obj.method()===obj;
在調用函數時設置this:call(),apply()和bind()
函數也是對象,因此,每個函數都有自己的方法
- call 方法第一個參數會給被調用函數內的this,剩下的參數作為參數傳入被調函數中
- apply 第一個參數是給被調函數內的this,第二個參數的個數組
- bind 展示了偏函數,=意味著它創建了一個新的函數,并用接下來的范式調用bind,this是thisValue,參數不斷的作為新的參數的參數
var jane={
name:'jane',
sayHelloTo:function(otherName){
'use strict':
console.log(this.name+'says hello to'+otherName);
}
}
jane.sayHelloTo('Tarzan');
jane.sayHelloTo.call(jane,'Tarzan')
var func=jane.sayHello func.call(jane,'Tarzan');
三者等價
jane.sayHelloTo('Tarzan')
jane.sayHelloTo.apply(jane,['Tarzan'])
var func=jane.sayHelloTo;
func.apply(jane,['Tarzan']);
三者等價
jane.sayHelloTo('Tarzan')
var func1=jane.sayHelloTo.bind(jane)
func1('Tarzan');
var func2=jane.sayHelloTo.bind(jane,'Tarzan');
三者等價
提取方法時丟失this
原因在于我們把counter.inc 的值作為函數來調用,因此this是全局對象我們執行的是window.counter++,而window.count不存在,所以會是undefined
var counter={
count:0
inc:function(){
this.count++;
}
}
正確的提前方法
var func3=counter.inc.bind(counter);
嵌套函數覆蓋this
var obj={
name:'Jane',
friend:['Tarzan','Cheeta'],
loop:function(){
'use strict';
this.friends.forEach(
function(friend){//1
console.log(this.name+' knows '+friend) //2
}
);
}
}
obj.loop();
//Cannot read property 'forEach' of undefined
因為1處的函數擁有自己的this,而this是undefined的
解決方案
- that ==this
that==this
loop:function(){
'use strict';
var that=this;
this.friends.forEach(
function(friend){//1
console.log(that.name+' knows '+friend) //2
}
);
}
- bind()
loop:function(){
'use strict';
var that=this;
this.friends.forEach(
function(friend){//1
console.log(that.name+' knows '+friend) //2
}.bind(this)
);
}
- forEach中的提供第二個參數this
loop:function(){
'use strict';
var that=this;
this.friends.forEach(
function(friend){//1
console.log(that.name+' knows '+friend) //2
},this);
}
三點操作符
(...)可以把數組轉化為實際參數
Math.max(...[13,7,30])
原型關系
對象通過內部屬性[[Prototype]]指定它的原型.每個對象都有這個屬性,而它也可以是null
var proto={
describe:function(){
return 'name :'+this.name
}
}
var obj={
[[prototype]]:proto,
name='obj'
}
使用prototype創建對象
創建原型為proto的對象
調用方法Object.create(proto,propDescObj?)
var PersonProto={
describe:function(){
return 'Person named'+this.name;
}
}
var jane=Object.create(PersonProto,{
name:{value:'Jane',writable:true}
})
通常只需創建空對象,然后手動添加屬性
var jane=Object.create(PersonProto)
jane.value='Jane'
讀取對象原型
Object.getPrototypeof(obj)
Object.getPrototypeof(jane)===PersonProto//true
檢查對象是否是另一個對象的原型
Object.prototype.isPrototypeOf(obj)
var A={}
var B=Object.create(A)
var C=Object.create(B)
A.isPrototypeOf(C)//true
C.isPrototypeOf(A)//false
找到定義屬性的對象,遍歷對象obj的原型鏈。該函數返回為propKey的自由屬性的第一個對象如果沒有返回null
function getDefiningObject(obj,propKey){
obj=Object(obj)
while(obj&&!{}.hasOwnProperty.call(obj,propKey)){
obj=Object.getPrototypeof(obj)
}
return obj;
}
特殊屬性protp,可以直接訪問[[Prototype]]
var obj={};
obj.__proto__===Object,prototype//true
obj.__proto__Array.prototype
Object.getPrototypeOf(obj)===Array.prototype//true
刪除只能刪除自有屬性,不能輸出繼承來的屬性
遍歷和檢測屬性
--
列出自有屬性
Object.getOwnPropertyNames(obj)//返回obj的所有自由屬性值
Object.key(obj)//返回obj的所以可枚舉的屬性值
getter和setter
對象字面量定義訪問權
var obj={
get foo(){
return 'getter';
},
set foo(value){
console.log('setter'+value);
}
}
obj.foo='bla'//setter:bla
obj.foo//;getter/
屬性描述符定義訪問權
var obj=Object.create({
Object.prototype.{
foo:{
get:function(){
return 'getter';
},
set:function(){
console.log('setter'+value);
}
}
}
})
屬性特性和屬性描述符
- 屬性特性是屬性的原子構建快
- 屬性描述符是一種數據結構,用于編程處理特性
屬于的屬性狀態,包括它的數據和元數據,都儲存在特性中它們是屬性擁有的字段,就像對象擁有的屬性。特效的鍵通常寫在雙方括號中。
- [[value]],持有屬性的值
- [[Writable]],是否可以被改變
- [[Get]],讀取屬性時調用
- [[Set]],設置調用
- [[Enumerable]]持有布爾值。設置一個屬性不可枚舉,在某些操作會隱藏此屬性
- [[Configurable]],持有布爾值,如果他是false,那么你不能刪除,改變屬性的任何特性,也不能把他從數據屬性轉化為訪問屬性
如屬性值123是只讀屬性
{
value:123
writable:false
enumberable:true
configurable:false
}
使用訪問其,也可以實現同樣的目的i
{
get:function(){return 123},
enumerable:true
configurable:false
}
通過屬性描述符獲取和設置屬性的特性
Object.getOwnPropertyDescriptor(Object.prototype,'toString')
改變propKey的屬性
Object.defineProperty(Obj,propKey,propDesc)
var obj=Object.defineProperty({},'foo',{
value:123
enumerable:true
})
批量處理
Object.defineProperties(obj,propDescObj)
var obj=Object.defineProperties({},{
foo:{value:123,enumerable:true},
bar:{value:'abc',enumerable:true}
})
保護對象
- 防止擴展
Object.preventExtensions(obj)
Object.isExtensible(obj)//檢查是否可以被擴展
- 封閉
防止擴展,并設置所以屬性"不可配置"
object.seal(obj)
Object.isSealed(obj)//是否是封閉的
- 凍結,
它使所有屬性不可寫,且封閉obj
Object.freeze(obj)
Object.isFrozen(obj)//查詢是否凍結
保護對象只影響自由屬性,不影響這些屬性的值
實例constructor屬性
默認函數包含一個實例原型對象
function C(){}
C.prototype.constructor===C//true
constructor屬性的用例
切換對象的構造函數
下面的catch子語句中,我們根據捕獲異常的構造函數不同,采取不同的處理
try{
....
}catch(e){
switch(e.constructor){
case SyntaxError;
...
break;
case CustomError;
...
break;
}
}
確定對象的構造函數名
function Foo(){}
var f=new Foo();
f.constructor.name//'foo'
創建相似對象,它和已有的對象x都有相同的構造函數: 可用于子構建函數實例的方法,且想要創建一個和this相似的新實例。這樣就不能使用一個固定的構造函數
function Constr(){}
var x=new Constr();
var y=new x.constructor();
console.log(y instanceof Constr);//true
指向父構造函數
一些繼承庫把父構造函數賦值給子構造函數的一個屬性 例如YUI框架通過Y.extend提供子類
function Super(){}
function Sub(){
Sub.superclass.sonstructor.call(this);
}
Y.extend(Sub,Super);
對象不是Object的實例
機會所以對象都是Object的實例,因為Object.prototype在這些對象的原型鏈上,但是也有對象不屬于這種情況
Object.create(null) instancefo Object//false
Object.prototype instanceof Object//false
在Web瀏覽器中,每一幀和窗口都有自己的域,具有獨立的全局變量。這使得instanceof不可用于這些跨域的對象。
原型屬性的數據
--
對于實例屬性,避免使用帶初始值的原型屬性
function Names(data){
if(data){
this.data==data;
}
}
Names.prototype.data=[];
var n1=new Names();
var n2=new Names();
n1.data.push("jane")
console.log(n1.data);
console.log(n2.data);
//都是jane
push改變了Names.prototype.data 的數組。因為這個數字被所以的沒有字有自有屬性的data的實例共享,所以n2也被影響了
根據需要創建實例屬性
function Names(data){
if(data){
this.data=data;
}
}
Names.prototype={
constructor:Names,
get data(){
Object.defineProperty(this,'data',{
value:[],
enumerable:true,
configurable:false,
writable:false
});
return this.data;
}
}
保持數據的私有化
- 構造函數環境中的私有化
- 帶有特殊標記鍵的屬性中的私有數據
- 具體化鍵的屬性中的私有數據
在調用構造函數時,創建了兩個東西:構造函數實例和環境。該實例有構造函數初始化,而該環境保持了構造函數的參數和局部變量。每個在構造函數內部創建的函數(包括方法)都會保存此環境(創建函數時的環境)的引用。由于保存了此環境的引用,即使在構造函數執行結束后,也仍然可以訪問這個環境。實例的三個值
- 公有屬性
- 私有值
- 特權方法(私有方法)
公有屬性
function Constr(...){
this.publicData=...;
}
私有值
function Constr(...){
...
var that=this;
var privateData=...;
function privateFunction(...){
privateData=...;
that.publicData=...;
that.publicMethod(...);
}
}
特權方法
function Constr(...){
...
this.privilegedMethod=function(...){
privateData=...;
privateFunction(...);
this.publicData=...;
this.publicMethod(...);
}
}
使用標記建保存私有數據
var KEY_BUFFER="_StringBuilder_buffer";
var StringBuilder=function(){
var KEY_BUFFER='_StringBuilder_buffer';
function StringBuilder(){
this[KEY_BUFFER]=[];
}
StringBuilder.prototype={
Constructor:StringBuilder,
add function(str){
this[KEY_BUFFER].push(str);
},
toString:function(){
return this[KEY_BUFFER].join('');
}
return StringBuilder;
}();
通過IIFE保持全局數據私有
var obj=function(){
var self={
publlicMethod:function(...){
privateData=...;
privvateFunction(...);
},
publicData:...
};
var privateData=...;
function privateFunction(){
privateData=...;
self.publicData=...;
self.publicMethod(...);
}
}();
稀疏數組和密集數組
含有空缺的數組稱為稀疏數組。不含空缺的數組稱為密集
var sparse=[,,'C'];
var dense=[undefined,undefined,'C'];
sparse.length//3
dense.length//3
0 in psarse//false
0 in dense//true
for(var i=0;i<sparse.length;i++)console.log(sparse[i])//undefined,undefined,c
for(var i=0;i<sparse.length;i++)console.log(dense[i])//undefined,undefined,c
forEach會跳過空缺但不跳過undefined元素
sparse.forEach(function(x){console.log(x)})//c
dense.forEach(function(x){console.log(x)})//undefined,undefined,c
數組遍歷
- forEach,every會跳過空缺
- map會跳過,但是保留空缺
- filter會去除空缺
- join會把空缺,undefined和null轉化為空字符串
- sort在排序時保留空缺
- apply把空缺轉化為undefined
數組原型方法
- Array.prototype.shift()//移除索引0處的元素并返回該元素。隨后元素的索引依次減1
- Array.prototype.unshift(eleml?,elem2?,...)//在數組最前面增加給定元素,返回新的數組
- Array.prototype.pop()移除最后一個元素并返回該元素
- Array.prototype.push(...)//在數組尾部添加給定元素
- Array.prototype.reverse()顛倒數組中的元素順序,并返回指向元數組的引用
- Array.prototype.sort()//數組排序,并返回排序后的數組
- Array.prototype.concat(arr1?,arr2?,...)//合并數組
- Array.prototype.join(sperator?)//對數組元素應用toString()創建字符串,并用sperator連接字符串
- Array.prototype.reduce(callback,initialValue)//從左到右迭代,并按照之前描述的調用回調函數。
JSON
JSON.stringify(value,replacer?,space?)
將JavaScript值value轉換為JSON字符串
function replacer(key,value){
if(typeof value==='number'){
value=2*value;
}
return value;
}
JSON.stringify({a:5,b:[2,8]},replacer)
//{a:10,b:[4,16]}
3
屬性鍵白名單,隱藏非數組對象內屬性
JSON.stringify({foo:1,bar:{foo:1,bar:1}},['bar']}//{bar:{bar:1}}