js小記:對象、原型及原型鏈、面向對象編程

1、js對象

一、js對象 
js對象是一種複合數據類型,它能夠把多個(不一樣類型的)數據集中在一個變量中,而且給每一個數據起名字。javascript

二、對象與數組 
對象的每一個數據有對應的名字(屬性名),咱們經過叫名字訪問具體哪一個數據; 
數組的每一個數據沒有名字,咱們只能經過編號來訪問具體哪一個數據。 
從本質講,數組就是對象的特殊形式,數組的每一個索引實質就是特殊化的 對象屬性名。舉個例子:php

var a = [0,1,2,3]; a['me'] = 1; a[-1] = '負數'; //負數 轉換爲 字符串 a[1.23] = '小數'; //小數 轉換爲 字符串 a['2.00'] = 'dad';//字符串 沒法轉換爲 整數,因此仍爲字符串 a['4'] = 'dad'; //字符串 轉換爲 整數 console.log(a); //[0, 1, 2, 3, "dad", me: 1, -1: "負數", 1.23: "小數", 2.00: "dad"] //由於 a['4'] = 'dad';,因此length值加1 console.log(a.length); //5 //上面例子說明數組的length屬性,僅計算有 整數索引 的值的個數
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

三、建立對象的兩種方式html

1)var o = new Object(); (2)var o = {}; (3)var o = new Object({x:0,y:0,radius:2}); (4)var ciclr = {x:0,y:0,radius:2};
  • 1
  • 2
  • 3
  • 4

對象 經過 構造器 生成:java

  • object(對象):下圖左邊的紅圈(o、a、d···)
  • constructor(構造器):下圖的右邊的紅圈(Object、Array···)

注:Object、Array等這些都是系統自帶的構造器,咱們也能夠自定義構造器(eg:下面深刻使用的 構造函數) 
這裏寫圖片描述編程

並非全部函數都當構造器使用 
eg: var o = new Math.min();api

四、初始化/訪問 數據數組

var book = new Object(); book.title = "math"; book.author = "bty"; book.chapter1 = { title:"math 簡介", };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

五、刪除對象屬性瀏覽器

1)delete book.chapter1; //完全刪除 (2)book.chapter1 = null; //僅僅置空
  • 1
  • 2

delete刪除成功或刪除的屬性不存在返回true.閉包

  • 數組:delete後 不是完全刪除,length不變
  • 對象:delete後 是 完全刪除,沒有length屬性
//數組 var a = [0,1,1,2]; console.log(a.length);//4 delete a[1]; console.log(a.length);//4 console.log(a); //[0, empty, 1, 2] console.log(a[1]); //undefined //對象 var b = {aaa:1,bbb:2}; console.log(b); //{aaa: 1, bbb: 2} delete b['aaa']; console.log(b); //{bbb: 2} console.log(b.length); //undefined //說明對象沒有length屬性
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

六、遍歷對象app

//key是obj裏的屬性名 for(var key in obj){ console.log(obj[key]); //console.log(obj.key);則會輸出undefined }
  • 1
  • 2
  • 3
  • 4
  • 5

其次還有some(),every(),map(),forEach(),filter()等方法。 
具體用哪一個應場景而定。


2、相關知識

一、this

下面講的仍是沒這裏好:Javascript中的this

(1)全局環境中的this: 
- 非嚴格模式:先指向undefined,而後再自動指向全局對象。 
- 嚴格模式:指向undefined 
如下均是 非嚴格模式。

例1:

//在瀏覽器中全局對象就是window。 var a = 10; alert(this.a); //10
  • 1
  • 2
  • 3

例2:

var a = 12; var obj = { a:1, b:this.a + 1 }; console.log(obj.b);//13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

特殊: 
new Function()裏面的this,不論它是在構造函數中,仍是函數調用中, 
this都指向 全局對象。

(function(){ var f = new Function('alert(this)'); f(); })(); //或 function Foo(){ this.bar = function(){ var f = new Function('alert(this)'); f(); } } var foo = new Foo(); foo.bar();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

(2)構造函數中的this:指向新建立的對象(紅圈部分) 
例1: 
這裏寫圖片描述

例2:

var a = 1; var obj = { a:2, b:function(){ function fun(){ return this.a; } console.log(fun()); } }; obj.b();//1
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

(3)函數中的this:指向函數的調用者(紅圈部分) 
這裏的調用者就是建立的對象p1,輸出:Hello,lily 
這裏寫圖片描述

在例如

var a = 1; var obj = { a:2, b:function(){ return this.a; } }; //函數的調用者是obj console.log(obj.b());//2
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

(4)eval()中的this:指向調用上下文中的this

//下面的閉包,這裏的this指向的是Globel (function(){ eval('alert(this)'); })();
  • 1
  • 2
  • 3
  • 4
//這裏的this是在bar函數裏調用的,因此這裏的this和bar函數裏面的this同樣。 //而bar函數裏的this指向的是其調用者foo對象 function Foo(){ this.bar = function(){ eval('alert(this)'); } } var foo = new Foo(); foo.bar();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

 

二、apply、call

看下面場景:

function Point(x,y){ this.x = x; this.y = y; this.move = function(x,y){ this.x += x; this.y += y; } } var point = new Point(0,0); point.move(1,1);//移動到(1,1) var circle = {x:0, y:0, r:1}; //移動圓至(1,2) point.move.apply(circle,[1,1]); //或point.move.call(circle,1,1);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

咱們首先new了一個point對象,初始座標(0,0),以後又將該點移動到(1,1)處。 
而後咱們又定義了circle對象,圓心座標(0,0),變徑1,以下圖: 
這裏寫圖片描述 
問題來了:如今咱們怎麼經過Point的move方法來讓 圓 移動到這個位置 
這裏寫圖片描述 
經過下面兩行任意一行代碼便可: 
point.move.apply(circle,[1,1]); 
point.move.call(circle,1,1);

apply和call實際只是將 Point裏的this指針的指向作了改變,point.move.call後,move方法開始指向circle對象。

call與apply區別: 
二者區別僅在參數上。apply第二個參數必須是個數組(把函數調用的值依次傳入),而call把參數依次傳入時用的不是數組,直接用逗號隔開而已,固然call第二個參數也能夠是數組

三、原型與原型鏈
function Teacher(){ this.courses = [];//至關於私有變量 } Teacher.prototype = { //至關於public constructor:Teacher, job:'teacher', setName = function(name){ this.name = name; }, addCourse = function(course){ this.courses.push(course); } } var bill = new Teacher(); bill.setName('Bill'); bill.addCourse('math'); var tom = new Teacher(); tom.setName('Tom'); tom.addCourse('physics');
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

上面代碼原型鏈以下: 
bill和tom都有一個隱式指針_ proto_,都指向Teacher.proptotype。 
又由於Teacher是一個函數對象,而函數對象又是經過new Function()來創建的,Function也有一個protoptye。Function.prototype屬性在js中是內置屬性。 
Teacher的_ proto_是個對象,同理推之如圖。

bill和tom是以Teacher.proptotype爲原型,而Teacher.proptotype又以Object爲原型。(這種原型繼承的方式就叫原型鏈) 
這裏寫圖片描述


原型鏈的操做

  • (1)屬性查找
  • (2)屬性修改
  • (3)屬性刪除

咱們繼續用上面的例子 
(1)屬性查找:

  • 訪問tom.toString() 
    首先js先從tom中找toString方法,發現沒有;而後再沿着原型鏈往上找,到Teacher.prototype中查找toString,發現也沒有,而後再到object.prototype中查找,發現有,而後調用toString方法

(2)屬性修改:

  • tom.name = ‘bty’; 
    首先js先在tom對象上查找有沒有name屬性,發現有,則直接將原來的’Tom’值改成’bty’。
  • tom.job = ‘assistant’; 
    首先js在tom對象上查找有沒有job屬性,發現沒有,則直接在tom對象中添加job屬性並賦值爲’assistant’ 
    若想修改原型上的屬性,則經過tom.prototype.job = 'assistant'修改,但這樣會讓全部Teacher建立出的對象job值都作修改。

(3)屬性刪除

  • delete tom.job; 
    假設tom.job = ‘assistant’已經執行。delete tom.job後會直接刪除tom上的job屬性,但不會刪除原型上的job屬性,此時再訪問tom.job,值爲teacher
  • delete tom.job; delete tom.job; 
    道理結果同上

判斷對象是否有該屬性: 
tom.hasOwnProperty(‘job’); 
請參考


原型的繼承

原型的繼承有兩種方式:

  • 一、用構造器(具體參見 3、深刻使用)
  • 二、用Object.create(); (低版本的ie瀏覽器不兼容)

下面就詳細說說Object.create(prototype, descriptors) 
詳細參見此處

var teacher = { job:'teacher', courses:[], setName = function(name){ this.name = name; }, addCourse = function(course){ this.courses.push(course); } }; var bill = Object.create(teacher); bill.setName('Bill'); bill.addCourse('math');
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

var bill = Object.create(teacher)即建立一個以teacher對象爲原型的bill對象。 
Object.create作的就是將bill的隱式指針_ proto_指向teacher,而不是指向teacher.prototype,這是一種新的原型繼承的方式。以下圖:

這裏寫圖片描述


3、深刻使用

一、構造函數 
建立構造器的三種形式:

  • function Person(){}
  • var Person = function(){}
  • var Person = new Function();

例:

//爲了和普通函數區分,構造函數首字母要大寫 function Rect(w,h){ this.width = w;this.height = h; this.area = function(){return this.width * this.height}; } var r = new Rect(5,10); alert(r.area()); //50 //過程:new一個對象出來,而後將對象交給了Rect這個函數; //new出來的這個對象在Rect中就叫this //換句話說,this指向的是新建立的對象
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

至於爲何不用下面代碼,

function Rect(w,h){ this.area = function(){return w * h}; } var r = new Rect(5,10); alert(r.area()); //50
  • 1
  • 2
  • 3
  • 4
  • 5

是爲了方便後續像r.width = 10;這樣修改數值

注意:

function Person(name,age){ this.name = name; this.age = age; return {}; ///結尾加了個return {},此時new對象時就會返回空對象,因此下面打印undefined } var my = new Person('bty','12'); console.log(my.age); //undefined
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

二、原型對象 
(1)簡單使用

function Person(){ Person.prototype.name = "Nicholas"; Person.prototype.age = 20; Person.prototype.sayName = function(){ alert(this.name); } } var person1 = new Person(); var person2 = new Person(); person1.sayName(); //Nicholas person2.sayName(); //Nicholas alert(person1.sayName === person2.sayName);//true person1.name = "Greg"; person1.sayName(); //sam//from instance person2.sayName(); //bty//from prototype
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

這裏寫圖片描述
(2)原型的問題 
若是咱們對原型屬性修改的是簡單變量類型(eg:數值或字符串),ok,沒問題。但若是修改是對象(eg:數組)就會出現問題。

function Person(){} Person.prototype = { //這些至關於 public 變量 constructor:Person, name:"bty", age:20, friends:["amy","sam"] }; var person1 = new Person(); var person2 = new Person(); person1.friends.push("ssh"); alert(person1.friends);//amy,sam,ssh alert(person2.friends);//amy,sam,ssh 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

咱們發現對person1.friends進行操做,person2.friends也跟着改變。說明他們是共享friends的(public變量),如今咱們怎麼將其變爲 私有變量 呢?繼續往下看。

三、組合原生和構造方法

function Person(name,age){ //這些是每一個對象擁有的屬性,至關於 private 變量 this.name = name; this.age = age; this.friends = ['amy','sam']; } Person.prototype = { //這些至關於 public 變量 constructor:Person, sayName:function(){ alert(this.name); } }; var person1 = new Person("bty",20); var person2 = new Person("ssh",12); person1.friends.push('zsn'); alert(person1.friends); //amy,sam,zsn alert(person2.friends); //amy,sam alert(person1.friends === person2.friends); //false alert(person1.sayName === person2.sayName); //true
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

4、全局對象(瀏覽器)

一、瀏覽器的全局對象是window 
二、瀏覽器中全部全局變量 實際就是 全局對象window的成員(屬性)

var value = 12; alert(window.value);//12
  • 1
  • 2

三、window.document表示瀏覽器窗口中的HTML頁面 
四、document.write()將內容寫入html頁面

for(x in window.document){ document.write(x) } //for(x in document){```}也行
  • 1
  • 2
  • 3
  • 4

5、面向對象編程

  • 全局變量
  • 封裝
  • 繼承
一、全局變量

定義方法: 
(1)在全局環境中 var a = ‘hhh’; 
(2)window.a = ‘hhh’; 
(3)(function(){ a = ‘hhh’})() //變量聲明提高

注意:

//這裏test爲全局變量。a爲局部變量 function todo(){ var a = test = 'hhh'; } //這裏test2爲全局變量,a二、b2爲局部變量(注意看標點符號) function todo2(){ var a2 = 'hhh', b2 = 'hhh'; test2 = 'hhh2'; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
二、封裝

經過prototype封裝public變量;經過直接在Aaa上聲明,封裝私有變量,而後咱們經過添加 this.XXX 方法,來訪問私有變量。

function Aaa(){ //至關於私有屬性 var _config = ['A','B','C']; this.getConfig = function(){ return _config; } } //至關於公有屬性 Aaa.prototype = { _step1:function(){}, _step2:function(){}, api:function(){} }; var my = new Aaa(); //外部沒法訪問config,必須經過this.XXX 方法以相似API的形式才能訪問config console.log(my.config);//undefined console.log(my.getConfig());//["A", "B", "C"]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
三、繼承
  • 原型繼承(JS固有的繼承) 
    -原型的繼承又兩種方式:Object.create()和構造器
  • 類繼承(模擬C、JAVA的繼承)

(1) 類繼承(模擬C、JAVA的繼承) 
咱們如今模擬:B繼承A

//(function(){})()是個閉包 (function(){ function ClassA(){ var classMethod = function(){}; } ClassA.prototype = { api:function(){} }; function ClassB(){ ClassA.apply(this,arguments); } ClassB.prototype = new ClassA(); ClassB.prototype = { constructor:ClassB,//由於以前constructor是ClassA,因此要改爲B api:function(){ ClassA.prototype.api.apply(this,arguments); } }; var b = new ClassB(); b.api(); })()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

這裏寫圖片描述

(2)原型繼承(JS固有的繼承)

// (function(){ var myproto = { action1:function(){}, action2:function(){}, }; //obj的隱式指針_ proto_直接指向了myproto var obj = Object.create(myproto); })();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

因爲IE低版本瀏覽器不支持Object.create(),因此咱們能夠寫個函數模擬下,以下:

var clone = (function(){ var F = function(){}; return function(proto){ F.prototype = proto; return new F(); } })();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

解釋:咱們建立函數F,再爲其指定原型,而F的原型就是傳入的proto對象的原型。最後在new F(),即最後建立了個新的對象,而這個對象是以F.proto爲原型,即以傳入對象proto爲原型

相關文章
相關標籤/搜索