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

相关文章
相关标签/搜索