JavaScript學習筆記(三)——this、原型、javascript面向對象

1、this

在JavaScript中this表示:誰調用它,this就是誰。html

JavaScript是由對象組成的,一切皆爲對象,萬物皆爲對象。this是一個動態的對象,根據調用的對象不一樣而發生變化,固然也可使用call、apply修改this指向的對象。它表明函數運行時,自動生成的一個內部對象,只能在函數內部使用java

1.一、JavaScript中函數與方法的區分

在面向過程的語言中咱們習慣把完成某個特定功能的代碼塊稱爲「函數」或「過程」,固然過程通常沒有返回值。在面嚮對象語言中咱們把對象的功能稱爲「方法」。但JavaScript是種介於面向對象與面向過程當中間的語言,一樣的方法有時是函數,有時是方法,以下所示:node

複製代碼
<script type="text/javascript">
            //1
            function show(){
                console.log("這是一個函數");
            }
            
            //2
            (function(){
                console.log("這是一個函數表達式");
            })();
            
            //3
            var obj1={
                show:function(){
                    console.log("這是一個方法");
                }
            };
            
            //4
            function obj2(){  //obj2是函數,構造函數
                this.show=function(){
                    console.log("這是一個方法,是一個表達式");
                }
            }
            var obj3=new obj2();
            
        </script>
複製代碼

能夠簡單的認爲若是調用時沒有經過對象沒有指定上下文則爲函數,不然爲方法。git

1.二、指向全局對象

當在所有範圍內使用 this,它將會指向全局對象。通常是window對象,但全局對象不必定只有window,特別是在node.js環境中。做爲函數調用時通常指向全局對象。github

複製代碼
        <script type="text/javascript">
            var name="tom";
            console.log(this.name);      //頂層對象,通常爲window    
            
            function show()
            {
                console.log(this.name);  //頂層對象,通常爲window
                return function(){
                    console.log(this.name);  //頂層對象,通常爲window,由於返回的是函數
                }
            }
            
            var f1=show();
            f1();
            
        </script>
複製代碼

運行結果:設計模式

1.三、做爲方法調用

當函數做爲方法調用時this指向調用該方法的對象。數組

複製代碼
            function show()
            {
                //當obj1.view()時this就是obj1,obj2.view()時this就是obj2
                console.log(this.name);
            }
            
            var obj1={name:"jack",view:show};
            obj1.view();
            
            var obj2={name:"mark",view:show};
            obj2.view();
複製代碼

運行結果:瀏覽器

示例代碼:閉包

複製代碼
        <script type="text/javascript">
            var name="lucy";
            this.name="Mali";
            function show()
            {
                //當obj1.view()時this就是obj1,obj2.view()時this就是obj2
                console.log(this.name);
                
                return function(){
                    console.log(this.name);  //這裏的this是調用時動態決定
                }
            }
            
            var obj1={name:"jack",view:show};
            obj1.view();
            
            var obj2={name:"mark",view:show};
            var f1=obj2.view();
            f1();  //由於f1屬於window(瀏覽器環境),調用f1時的this也就是window
            
        </script>
複製代碼

運行結果:

示例代碼:

複製代碼
        <script type="text/javascript">
            var name="lucy";
            this.name="Mali";
            function show()
            {
                //當obj1.view()時this就是obj1,obj2.view()時this就是obj2
                console.log(this.name);
                that=this;  //閉包
                return function(){
                    console.log(that.name);  //這裏的that指向的是外層函數調用時的對象
                }
            }
            
            var obj1={name:"jack",view:show};
            obj1.view();
            
            var obj2={name:"mark",view:show};
            var f1=obj2.view();
            f1();            
        </script>
複製代碼

運行結果:

1.四、在構造函數中的this

構造函數中的this指向新建立的對象,new出誰this就是誰。

示例代碼:

複製代碼
<script type="text/javascript">
            this.name="吉娃娃";
            function Dog(name)
            {
                this.name=name;
                this.bark=function(){
                    console.log(this.name+"在叫,汪汪...");
                }
            }
            
            var dog1=new Dog("哈士奇");
            dog1.bark();
</script>
複製代碼

運行結果:

按照嚴格的語法,構造函數不該該返回值,可是JavaScript是容許構造方法返回值的,默認返回this,修改後的示例以下:

複製代碼
            this.name="吉娃娃";
            function Dog(name)
            {
                this.name=name;
                this.bark=function(){
                    console.log(this.name+"在叫,汪汪...");
                }
                return this.bark;
            }
            
            var dog1=new Dog("哈士奇");
            dog1();
複製代碼

運行結果:

 

1.五、指定this指向的對象

當調用方法是經過Function.call,或Function.apply時this爲調用時指定的對象,若是沒有指定則爲頂層對象,瀏覽器中是window。

示例代碼:

複製代碼
            this.name="伽啡";
            function Cat(name)
            {
                this.name=name;
            }
            var bark=function(n){
                console.log(this.name+"叫了"+(n||1)+"聲喵喵...");
            }
            
            var cat1=new Cat("波斯");
            var cat2=new Cat("龍貓");
            
            bark.call(cat1,5);  //調用時this是cat1
            bark.apply(cat2,[8]);  //調用時this是cat2
            bark.apply(window,[9]);  //調用時this是window
            bark.apply();  //調用時this是頂層對象
複製代碼

 

運行結果:

1.六、做爲事件時的this

若是頁面中的元素與事件進行綁定時事件中的this通常指向網頁元素,如按鈕,文本框等。可是在元素中直接指定要執行的函數則this指向window頂層對象。

複製代碼
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>this</title>
    </head>
    <body>
        <input type="button" value="按鈕A" class="btn"/>
        <input type="button" value="按鈕B" class="btn"/>
        <input type="button" value="按鈕C" class="btn"/>
        <input type="button" value="按鈕D" onclick="handler()"/>
        <!--handler中的this指向window或頂層對象-->
        <input type="button" value="按鈕E" id="btnE"/>
        <script type="text/javascript">
            var buttons = document.querySelectorAll(".btn");
            
            for (var i=0;i<buttons.length;i++) {
                //handler中的this指向按鈕對象
                buttons[i].onclick=handler;
            }
            
            function handler(){
                    alert(this.value);
            }
            
            //handler中的this指向btnE
            document.getElementById("btnE").addEventListener("click",handler,false);
        </script>
    </body>
</html>
複製代碼

當點擊A-C時的效果:

當點擊D時的效果:

 

當點擊按鈕E時的效果:

複製代碼
<!DOCTYPE html>
<html>

    <head>
        <meta charset="UTF-8">
        <title>this</title>
    </head>

    <body>
        <input type="button" value="按鈕F的值" id="btnF" />
        <input type="button" value="按鈕G的值" id="btnG" onclick="app.show()"/>
        <script type="text/javascript">
            value="window的值"
            var app = {
                value: "app的值",
                show: function() {
                    alert(this.value); //若是show爲事件,則this指向觸發事件的對象
                },
                init: function() {
                    //handler中的this指向btnE
                    document.getElementById("btnF").addEventListener("click", this.show, false);
                }
            };
            
            app.show();   //"app的值",show方法中的this指向app這個對象
            app.init();     //init中的this指向誰app對象
            
            
        </script>
    </body>
</html>
複製代碼

 加載時運行結果:

點擊按鈕F時的效果:

點擊按鈕G時的效果:

 

在HTML元素上直接指定事件嚴格來講都不能說是事件綁定,只能描述是當按鈕點擊完成後執行的函數。若是想將執行時的對象帶回,能夠增長參數this。

1.七、小結

函數調用能夠以下幾種基本形式:

1)、fun(x,y);

2)、obj.fun(x,y);

3)、fun.apply(obj,[x,y]);

4)、fun.call(obj,x,y);

第1,2種調用的方式最終將轉換成3,4方法,也就是說1,2只是一種簡化的語法糖,那麼this就是apply與obj的第1個參數,指向調用時的上下文。

2、原型(prototype)

JavaScript是一種經過原型實現繼承的語言與別的高級語言是有區別的,像java,C#是經過類型決定繼承關係的,JavaScript是的動態的弱類型語言,總之能夠認爲JavaScript中全部都是對象,在JavaScript中,原型也是一個對象,經過原型能夠實現對象的屬性繼承JavaScript的對象中都包含了一個" prototype"內部屬性,這個屬性所對應的就是該對象的原型。

"prototype"做爲對象的內部屬性,是不能被直接訪問的。因此爲了方便查看一個對象的原型,Firefox和Chrome內核的JavaScript引擎中提供了"__proto__"這個非標準的訪問器(ECMA新標準中引入了標準對象原型訪問器"Object.getPrototype(object)")。

1.一、爲何須要prototype

如今有一個Student構造函數,經過new調用該構造函數能夠建立一個新的對象,示例以下:

複製代碼
            //構造方法,用於建立學生對象
            function Student(name) {
                this.name = name;
            }
            
            var tom=new Student("tom");
            tom.show=function(){
                console.log(this.name);
            }
            
            var rose=new Student("rose");
            rose.show=function(){
                console.log(this.name);
            }
                        
            tom.show();
            rose.show();
複製代碼

運行結果:

上面的示例中tom與rose都須要show方法,建立對象後咱們直接爲兩個對象分別都增長了show方法,可是這樣作的弊端是若是再增長更多對象也須要添加show方法,最好的辦法是修改構造方法Student,以下所示:

複製代碼
            //構造方法,用於建立學生對象
            function Student(name) {
                this.name = name;
                this.show = function() {
                    console.log(this.name);
                }
            }

            var tom = new Student("tom");
            var rose = new Student("rose");

            tom.show();
            rose.show();
複製代碼

可是若是Student構造函數是一個內置的函數或是其它框架定義的類型,則修改就比較麻煩了,能夠不修改源碼的狀況擴展該對象的原型也能夠達到目的,以下所示:

複製代碼
            //構造方法,用於建立學生對象
            function Student(name) {
                this.name = name;
            }
            
            //修改Student對象的原型,增長show方法
            Student.prototype.show = function() {
                console.log(this.name);
            }
            
            var tom = new Student("tom");
            var rose = new Student("rose");

            tom.show();
            rose.show();
複製代碼

在示例中咱們並無修改Student這個構造函數而是修改了了Student的原型對象,相似它的父類,以下圖所示:

此時你也許會認爲全部的Object都增長了show方法,這樣想是錯誤的,由於Student的原型是一個對象而不是像其它高級語中的類型,咱們修改的只是Student的原型對象,不會影響其它的對象。

複製代碼
            var mark=new Object();
            mark.name="mark";
            
            var lucy={name:"lucy"};
            //mark.show();
            //lucy.show();
複製代碼

此處的兩個show方法是錯誤的。總之原型的主要做用就是爲了實現繼承與擴展對象。

1.二、typeof與instanceof

1.2.一、typeof

在 JavaScript 中,判斷一個變量的類型能夠用typeof,如:

複製代碼
             var str1="Hello";
             var str2=new String("Hello");
             
            console.log(typeof 1);
            console.log(typeof(true));
            console.log(typeof str1);
            console.log(typeof str2);
            console.log(typeof([1,2,3]));
            console.log(typeof Date);
            console.log(typeof undefined);
            console.log(typeof null);
            console.log(typeof zhangguo);
複製代碼

運行結果:

1)、數字類型, typeof 返回的值是 number。好比說:typeof(1),返回值是number
2)、字符串類型, typeof 返回的值是 string。好比typeof("123")返回值是string。 
3)、布爾類型, typeof 返回的值是 boolean 。好比typeof(true)返回值是boolean。
4)、對象、數組、null 返回的值是 object 。好比typeof(window),typeof(document),typeof(null)返回的值都是object。
5)、函數類型,返回的值是 function。好比:typeof(eval),typeof(Date)返回的值都是function。
6)、不存在的變量、函數或者undefined,將返回undefined。好比:typeof(abc)、typeof(undefined)都返回undefined。

1.2.二、instanceof

使用 typeof 運算符時採用引用類型存儲值會出現一個問題,不管引用的是什麼類型的對象,它都返回 "object"。ECMAScript 引入了另外一個 Java 運算符 instanceof 來解決這個問題。instanceof 運算符與 typeof 運算符類似,用於識別正在處理的對象的類型。與 typeof 方法不一樣的是,instanceof 方法要求開發者明確地確認對象爲某特定類型。

instanceof用於判斷某個對象是否被另外一個函數構造。

例如:
var oStringObject = new String("hello world"); 
console.log(oStringObject instanceof String); // 輸出 "true"

複製代碼
             var str1="Hello";
             var str2=new String("Hello");
             
            console.log(str1 instanceof String);   //string false
            console.log(str2 instanceof String);  //object true
            
            console.log(1 instanceof Number); //false
            
            function Foo(){};
            
            var f1=new Foo();
            console.log(f1 instanceof Foo);
複製代碼

運行結果:

1.三、Function與Object

1.3.一、Function

函數就是對象,表明函數的對象就是函數對象。全部的函數對象是被Function這個函數對象構造出來的。Function是最頂層的構造器。它構造了系統中全部的對象,包括用戶自定義對象,系統內置對象,甚至包括它自已。這也代表Function具備自舉性(自已構造本身的能力)。這也間接決定了Function的call和constructor邏輯相同。每一個對象都有一個 constructor 屬性,用於指向建立其的函數對象。

先來看一個示例:

複製代碼
            function Foo(a, b, c) {  //Foo這個構造函數由Function構造,函數也是對象
                return a * b * c;
            }
            
            var f1=new Foo();   //f1由Foo構造,Foo是一個構造函數,能夠理解爲類
            
            console.log(Foo.length);   //參數個數 3
            console.log(typeof Foo.constructor);  //function
            console.log(typeof Foo.call);  //function
            console.log(typeof Foo.apply);  //function
            console.log(typeof Foo.prototype);  //object object
複製代碼

運行結果:

 

函數與對象具備相同的語言地位
沒有類,只有對象
函數也是一種對象,所謂的函數對象
對象是按引用來傳遞的

複製代碼
            function Foo() {};
            var foo = new Foo();
            //Foo爲foo的構造函數,簡單理解爲類型
            console.log(foo instanceof Foo); // true
            
            //可是Function並非foo的構造函數
            console.log(foo instanceof Function); // false
            
            //Function爲Foo的構造函數
            alert(Foo instanceof Function); //true
複製代碼

上面的代碼解釋了foo和其構造函數Foo和Foo的構造函數Function的關係,Foo是由Function構造獲得,能夠簡單理解爲,系統中有一個這樣的構造函數:

複製代碼
            function Function(name,body)
            {
            }
            
            var Foo=new Function("Foo","");
複製代碼

1.3.二、Object

對於Object它是最頂層的對象,全部的對象都將繼承Object的原型,可是你也要明確的知道Object也是一個函數對象,因此說Object是被Function構造出來的。

複製代碼
alert(Function instanceof Function);//true 
alert(Function instanceof Object);//true 
alert(Object instanceof Function);//true 

function Foo() {};
var foo = new Foo();
alert(foo instanceof Foo); // true
alert(foo instanceof Function); // false
alert(foo instanceof Object); // true
alert(Foo instanceof Function); // true
alert(Foo instanceof Object); // true
複製代碼

 

JavaScript 原型鏈

複製代碼
            function A() {this.x="x";};
            var a=new A();
            
            function B() {this.y="y"};
            B.prototype=a;
            var b=new B();
            
            function C() {this.z="z"};
            C.prototype=b;
            var c=new C();
            
            console.log(c instanceof C);
            console.log(c instanceof B);
            console.log(c instanceof A);
            console.log(c instanceof Object);
            
            console.log(c.x+","+c.y+","+c.z);
複製代碼

運行結果:

1.四、經過prototype擴展對象

JavaScript內置了不少對象,如Array、Boolean、Date、Function、Number、Object、String 

假設咱們想給String類型增長一個repeat方法,實現重複字符,如"a".rpt(),則將輸出aa,"a".rpt(5),輸出「aaaaa」,由於String是JavaScript中內置的對象,能夠經過修改該對象的原型達到目的:

複製代碼
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>prototype原型</title>
    </head>
    <body>
        <script type="text/javascript">
            var v="hi";
            
            String.prototype.rpt=function(n)
            {
                n=n||2;
                var temp="";
                for(var i=0;i<n;i++) temp+=this;
                return temp;
            }
            
            console.log(v.rpt());
            console.log(v.rpt(10));
        </script>
    </body>
</html>
複製代碼

運行結果:

示例中給String對象的原型增長了一個rpt方法,全部的String都是衍生自String.prototype,那全部的String對象都將得到rpt方法。

複製代碼
            //擴展String類型,增長trim方法用於刪除字符串的首尾空格
            String.prototype.trim=function()
            {
                return this.replace(/^\s+|\s+$/igm,'');
            }
            
            console.log("[begin]"+"  Hello JavaScript  ".trim()+"[end]");
複製代碼

運行結果:

爲了擴展更加方便,能夠修改Function的原型,給每個函數衍生的對象增長方法method用於擴展。

複製代碼
            //修改函數對象的原型,添加method方法,擴展全部的函數
            Function.prototype.method=function(name,fun){
                //若是當前擴展的函數的原型中不包含名稱爲name的對象
                if(!this.prototype[name]){
                    //添加
                    this.prototype[name]=fun;
                }
            }
            
            String.method("show",function(){
                console.log("輸出:"+this);
            });
            
            "Hello".show();
            "JavaScript".show();
複製代碼

運行結果:

1.五、經過prototype調用函數

咱們能夠經過對象調用某個方法,由於這個對象的原型中已經定義好了該方法,其實咱們經過原型也能夠直接調用某個方法,有些方法只存在原型中,只有當前類型關聯了該原型才能夠得到該方法,但有時咱們須要使用該方法去處理非該原型下的對象,如:

複製代碼
            function add(x,y,z)
            {
                var array1=[].slice.call(arguments,0,3);
                console.log(array1);
                var array2=Array.prototype.slice.apply(arguments,[0,3]);
                console.log(array2);
            }
            add(1,2,8,9,10);
複製代碼

運行結果:

示例代碼:

複製代碼
            var str1 = "Hello JavaScript";
            console.log(str1.toUpperCase()); //傳統的調用辦法

            var str2=String.prototype.toUpperCase.apply(str1); //經過原形直接調用
            console.log(str2);
            
            var str3=String.prototype.toUpperCase.call(str1); //經過原形直接調用
            console.log(str3);
            
            var array1=[2,3,5,7,8,9,10];
            var array2=array1.slice(1,3);
            console.log(array2);
            
            var slice=Array.prototype.slice;
            console.log(slice.apply(array1,[1,3]));//經過原形直接調用
            console.log(slice.call(array1,1,3));
複製代碼

運行結果:

練習:擴展Date,增長一個顯示中文日期的方法,如:new Date().trandition(),輸出2016年08月09日 23點15分18秒;使用prototype的方式間接調用該方法。

3、JavaScript面向對象(OOP)

3.一、封裝

複製代碼
            function Animal()
            {
                this.name="動物";
                this.getName=function(){
                    return this.name;
                }
                this.setName=function(name){
                    this.name=name;
                }
                this.bark=function(){
                    console.log(this.name+"在叫...");
                }
            }
            var animal=new Animal();
            animal.setName("小狗");
            animal.bark();
複製代碼

這裏定義的一個構造函數,將name封裝成屬性,Animal函數自己用於封裝動物的屬性與行爲,運行結果:

3.二、繼承

JavaScript的繼承的實現主要依靠prototype(原型)來實現,再增長兩個動物,如狗與貓:

複製代碼
            function Animal()
            {
                this.name="動物";
                this.getName=function(){
                    return this.name;
                }
                this.setName=function(name){
                    this.name=name;
                }
                this.bark=function(){
                    console.log(this.name+"在叫...");
                }
            }
            
            function Dog(){
                this.hunt=function(){console.log("狗在捕獵");};
            }
            //指定Dog構造函數的原型對象,簡單理解爲父類
            Dog.prototype=new Animal();
            
            function Cat(){
                this.hunt=function(){console.log("貓在抓老鼠");};
            }
            Cat.prototype=new Animal();
            
            var dog=new Dog();
            dog.bark();
            dog.hunt();
            
            var cat=new Cat();
            cat.bark();
            cat.hunt();
複製代碼

運行結果:

3.三、多態

java與C#中的多態主要體如今重載與重寫上,由於JavaScript是弱類型的,類方法參數是動態指定的因此並無真正意義上的重載,只能在方法中判斷參數達到目的。

複製代碼
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>JavaScript面向對象</title>
    </head>
    <body>
        <script type="text/javascript">
            function Animal()
            {
                this.name="動物";
                this.getName=function(){
                    return this.name;
                }
                this.setName=function(name){
                    this.name=name;
                }
                this.bark=function(){
                    console.log(this.name+"在叫...");
                }
            }
            
            function Dog(){
                this.hunt=function(){console.log("狗在捕獵");};
            }
            //指定Dog構造函數的原型對象,簡單理解爲父類
            Dog.prototype=new Animal();
            //重寫原型對象中的bark方法
            Dog.prototype.bark=function(){
                console.log("汪汪...");
            }
            
            function Cat(){
                this.hunt=function(){console.log("貓在抓老鼠");};
            }
            Cat.prototype=new Animal();
            //重寫原型對象中的bark方法
            Cat.prototype.bark=function(){
                console.log("喵喵...");
            }
            
            function play(animal)
            {
                animal.bark();
                animal.hunt();
            }
            
            var dog=new Dog();
            var cat=new Cat();
            play(dog);
            play(cat);
        </script>
    </body>
</html>
複製代碼

運行結果:

 

練習:請使用javascript完成一個簡單工廠設計模式。

4、示例下載

https://github.com/zhangguo5/javascript003.git

參照:http://www.cnblogs.com/best

相關文章
相關標籤/搜索