JavaScript学习总括
分类:pc28.am

javascript 是一种 prototype based programming 的语言, 而与我们通常的 class based programming 有很大 的区别,我列举重要的几点如下:

一、this

在JavaScript中this表示:谁调用它,this就是谁。

JavaScript是由对象组成的,一切皆为对象,万物皆为对象。this是一个动态的对象,根据调用的对象不同而发生变化,当然也可以使用call、apply修改this指向的对象。它代表函数运行时,自动生成的一个内部对象,只能在函数内部使用

用过JavaScript的同学们肯定都对prototype如雷贯耳,但是这究竟是个什么东西却让初学者莫衷一是,只知道函数都会有一个prototype属性,可以为其添加函数供实例访问,其它的就不清楚了,最近看了一些 JavaScript高级程序设计,终于揭开了其神秘面纱。

1.函数是first class object, 也就是说函数与对象具有相同的语言地位2.没有类,只有对象3.函数也是一种对象,所谓的函数对象4.对象是按 引用 来传递的那么这种 prototype based programming 的语言如何实现继承呢, 这也便是 prototype 的由来.

1.0、猜猜答案

代码如下:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>JavaScript this</title>
    </head>
    <body>
        <h2>JavaScript this</h2>
        <input type="button" value="按钮A" id="btnA"/>
        <input type="button" value="按钮B" id="btnB" onclick="sayHello()"/>
        <script type="text/javascript">
            function sayHello(){
                alert("Hello Button!");
                console.log(this);
            };

            document.getElementById("btnA").onclick=sayHello;

        </script>
    </body>
</html>

请问输出到控制台的结果是什么?相同吗?输出两个什么对象?

答案:

图片 1图片 2

<input type="button" value="按钮A" id="btnA"/>
Window

View Code

每个函数都有一个prototype属性,这个属性是指向一个对象的引用,这个对象称为原型对象,原型对象包含函数实例共享的方法和属性,也就是说将函数用作构造函数调用的时候,新创建的对象会从原型对象上继承属性和方法。不像传统的面向对象语言,Javascript的继承机制基于原型,而不是Class类。

function foo{return a*b*c;}alert;alert(typeof foo.constructor);alert;alert;alert;

1.1、JavaScript中函数与方法的区分

在面向过程的语言中我们习惯把完成某个特定功能的代码块称为“函数”或“过程”,当然过程一般没有返回值。在面向对象语言中我们把对象的功能称为“方法”。但JavaScript是种介于面向对象与面向过程中间的语言,同样的方法有时是函数,有时是方法,如下所示:

<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>

可以简单的认为如果调用时没有通过对象没有指定上下文则为函数,否则为方法。

1、私有变量、函数在具体说prototype前说几个相关的东东,可以更好的理解prototype的设计意图。在了解JavaScript原型链之前,有必要先了解一下JavaScript的作用域链。JavaScript的函数作用域,在函数内定义的变量和函数如果不对外提供接口,那么外部将无法访问到,也就是变为私有变量和私有函数。

对于上面的代码,用浏览器运行后你会发现:

1.2、指向全局对象

当在全部范围内使用 this,它将会指向全局对象。一般是window对象,但全局对象不一定只有window,特别是在node.js环境中。作为函数调用时一般指向全局对象。

        <script type="text/javascript">
            var i=5;
            i=6;
            console.log(i);   //6
            console.log(window.i);  //6
            console.log(this.i);  //6
            console.log(this);  //window
        </script>

结果:

图片 3

        <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>

运行结果:

图片 4

function Obj(){ var a=0; //私有变量 var fn=function(){ //私有函数 }}

1.length: 提供的是函数的参数个数2.prototype: 是一个object3.其它三个都是function而对于任何一个函数的声明,它都将会具有上面所述的5个property.

1.3、作为方法调用

当函数作为方法调用时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();

运行结果:

图片 5

示例代码:

        <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>

运行结果:

图片 6

示例代码:

        <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>

运行结果:

图片 7

这样在函数对象Obj外部无法访问变量a和函数fn,它们就变成私有的,只能在Obj内部使用,即使是函数Obj的实例仍然无法访问这些变量和函数

下面我们主要看下prototype.

1.4、在构造函数中的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>

运行结果:

图片 8

按照严格的语法,构造函数不应该返回值,但是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();

运行结果:

 图片 9

var o=new Obj; //undefinedconsole.log; //undefined
// prototypefunction Person{this.name = name;this.gender = gender;this.whoAreYou = function(){//这个也是所谓的closure, 内部函数可以访问外部函数的变量var res = "I'm "   this.name   " and I'm a "   this.gender  ".";return res;};}// 那么在由Person创建的对象便具有了下面的几个属性Person.prototype.age = 24;Person.prototype.getAge = function(){return this.age;};flag = true;if {var fun = new Person;alert;alert;alert;alert;}Person.prototype.salary = 10000;Person.prototype.getSalary = function(){return this.name   " can earn about "   this.salary   "RMB each month." ;};// 下面就是最神奇的地方, 我们改变了Person的prototype,而这个改变是在创建fun之后// 而这个改变使得fun也具有了相同的属性和方法// 继承的意味即此if {alert;alert(fun.constructor.prototype.age);//而这个相当于你直接调用 Person.prototype.agealert;}

1.5、指定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是顶层对象

 

运行结果:

图片 10

2、静态变量、函数

从上面的示例中我们可以发现,对于prototype的方法或者属性,我们可以 动态地 增加, 而由其创建的 对象自动会 继承 相关的方法和属性.

1.6、作为事件时的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时的效果:
图片 11

当点击D时的效果:

 图片 12

当点击按钮E时的效果:

图片 13

<!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>

 加载时运行结果:

图片 14

点击按钮F时的效果:

图片 15

点击按钮G时的效果:

 图片 16

在HTML元素上直接指定事件严格来说都不能说是事件绑定,只能描述是当按钮点击完成后执行的函数。如果想将执行时的对象带回,可以增加参数this。

当定义一个函数后通过 “.”为其添加的属性和函数,通过对象本身仍然可以访问得到,但是其实例却访问不到,这样的变量和函数分别被称为静态变量和静态函数,用过Java、C#的同学很好理解静态的含义。

另外,每个对象都有一个 constructor 属性,用于指向创建其的函数对象,如上例中的 fun.constructor 指向的 就是 Person.

1.7、小结

函数调用可以如下几种基本形式:

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个参数,指向调用时的上下文。

function Obj(){} Obj.a=0; //静态变量 Obj.fn=function(){ //静态函数 }console.log; //0console.log; //functionvar o=new Obj; //undefinedconsole.log; //undefined

那么一个疑问就自然产生了, 函数对象中自身声明的方法和属性与prototype声明的对象有什么差别?

二、原型(prototype)与原型链

在javascript面向对象中关于原型(prototype)、原型链、__proto__、Function、Object等内容是较难理解的,先上几张便于大家理解的图:

JavaScript藏宝图

图片 17

JavaScript Object Layout:

图片 18

Object Relationship Diagram with Inheritance

图片 19

图片 20

JavaScript是一种通过原型实现继承的语言与别的高级语言是有区别的,像java,C#是通过类型决定继承关系的,JavaScript是的动态的弱类型语言,总之可以认为JavaScript中所有都是对象,在JavaScript中,原型也是一个对象,通过原型可以实现对象的属性继承JavaScript的对象中都包含了一个" prototype"内部属性,这个属性所对应的就是该对象的原型。

"prototype"作为对象的内部属性,是不能被直接访问的。所以为了方便查看一个对象的原型,Firefox和Chrome内核的JavaScript引擎中提供了"__proto__"这个非标准的访问器(ECMA新标准中引入了标准对象原型访问器"Object.getPrototype(object)")。

3、实例变量、函数

1.自身声明的方法和属性是 静态的, 也就是说你在声明后,试图再去增加新的方法或者修改已有的方法,并不会 对由其创建的对象产生影响, 也即 继承 失败2.而prototype可以动态地增加新的方法或者修改已有的方法, 从而是 动态的 ,一旦 父函数对象 声明了相关 的prototype属性,由其创建的对象会 自动继承 这些prototype的属性.继续上面的例子:

1.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();

运行结果:

图片 21

上面的示例中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的原型对象,类似它的父类,如下图所示:

图片 22

此时你也许会认为所有的Object都增加了show方法,这样想是错误的,因为Student的原型是一个对象而不是像其它高级语中的类型,我们修改的只是Student的原型对象,不会影响其它的对象。

            var mark=new Object();
            mark.name="mark";

            var lucy={name:"lucy"};
            //mark.show();
            //lucy.show();

此处的两个show方法是错误的。总之原型的主要作用就是为了实现继承与扩展对象。

在JavaScript中,原型也是一个对象,通过原型可以实现对象的属性继承,JavaScript的对象中都包含了一个”[[Prototype]]”内部属性,这个属性所对应的就是该对象的原型。

“[[Prototype]]”作为对象的内部属性,是不能被直接访问的。所以为了方便查看一个对象的原型,Firefox和Chrome中提供了__proto__这个非标准(不是所有浏览器都支持)的访问器(ECMA引入了标准对象原型访问器”Object.getPrototype(object)”)。在JavaScript的原型对象中,还包含一个”constructor”属性,这个属性对应创建所有指向该原型的实例的构造函数

 

在面向对象编程中除了一些库函数我们还是希望在对象定义的时候同时定义一些属性和方法,实例化后可以访问,JavaScript也能做到这样

flag = true;// 函数内部声明的方法是静态的,无法传递的Person.school = "ISCAS";Person.whoAreYou = function(){return "zhutao";};//动态更改声明期的方法,并不会影响由其创建的对象的方法, 即所谓的 静态if {alert;alert;//输出的是 "undefined"alert; //输出 zhutaoalert; // I'm Tower and I'm a male.}Person.prototype.getSalary = function(){return "I can earn 1000000 USD";};if {alert;//已经继承了改变, 即所谓的 动态}

1.2、typeof与instanceof

function Obj(){ this.a=[]; //实例变量 this.fn=function(){ //实例方法 }}console.log; //undefinedconsole.log; //undefinedvar o=new Obj();console.log; //objectconsole.log; //function

既然有函数对象本身的属性, 也有prototype的属性, 那么是由其创建的对象是如何搜索相应的属性的呢?

1.2.1、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);

运行结果:

图片 23

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.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);

运行结果:

图片 24

function Obj(){ this.a=[]; //实例变量 this.fn=function(){ //实例方法 }}var o1=new Obj;o1.fn={};console.log; //[1]console.log; //objectvar o2=new Obj; //[]console.log; //function

1.先去搜索函数对象本身的属性,如果找到立即执行2.如果1没有找到,则会去搜索prototype属性,有2种结果,找到则直接执行,否则继续搜索 父对象 的 父对象 的prototype, 直至找到,或者到达 prototype chain 的结尾上面也回答如果函数对象本身的属性与prototype属性相同时的解决方式, 函数本身的对象 优先 .

1.3、Function与Object

上面的代码运行结果完全符合预期,但同时也说明一个问题,在o1中修改了a和fn,而在o2中没有改变,由于数组和函数都是对象,是引用类型,这就说明o1中的属性和方法与o2中的属性与方法虽然同名但却不是一个引用,而是对Obj对象定义的属性和方法的一个复制。

prototype 的典型示例

1.3.1、Function

函数就是对象,代表函数的对象就是函数对象。所有的函数对象是被Function这个函数对象构造出来的。Function是最顶层的构造器。它构造了系统中所有的对象,包括用户自定义对象,系统内置对象,甚至包括它自已。这也表明Function具有自举性(自已构造自己的能力)。这也间接决定了Function的call和constructor逻辑相同。每个对象都有一个 constructor 属性,用于指向创建其的函数对象。

图片 25

先来看一个示例:

            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

运行结果:

 图片 26

函数与对象具有相同的语言地位
没有类,只有对象
函数也是一种对象,所谓的函数对象
对象是按引用来传递的

            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","");

如上面例子中的构造函数,JavaScript中函数也是对象,所以就可以通过_proto_查找到构造函数对象的原型。

Function对象作为一个函数,就会有prototype属性,该属性将对应”function () {}”对象。

Function对象作为一个对象,就有__proto__属性,该属性对应”Function.prototype”,也就是说,”Function._proto_ === Function.prototype”。

在这里对“prototype”和“proto”进行简单的介绍:

对于所有的对象,都有__proto__属性,这个属性对应该对象的原型。

对于函数对象,除了__proto__属性之外,还有prototype属性,当一个函数被用作构造函数来创建实例时,该函数的prototype属性值将被作为原型赋值给所有对象实例(也就是设置实例的__proto__属性)

图片 27

这个对属性来说没有什么问题,但是对于方法来说问题就很大了,因为方法都是在做完全一样的功能,但是却又两份复制,如果一个函数对象有上千和实例方法,那么它的每个实例都要保持一份上千个方法的复制,这显然是不科学的,这可肿么办呢,prototype应运而生。先看看对象的含义:

用过 jQuery 或者 Prototype 库的朋友可能知道,这些库中通常都会有 trim 这个方法。

1.3.2、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);

运行结果:

图片 28

当查找一个对象的属性时,JavaScript 会向上遍历原型链,直到找到给定名称的属性为止,到查找到达原型链的顶部(也就是 Object.prototype),如果仍然没有找到指定的属性,就会返回 undefined。

Object对象本身是一个函数对象。既然是Object函数,就肯定会有prototype属性,所以可以看到”Object.prototype”的值就是”Object {}”这个原型对象。反过来,当访问”Object.prototype”对象的”constructor”这个属性的时候,就得到了Obejct函数。
另外,当通过”Object.prototype._proto_”获取Object原型的原型的时候,将会得到”null”,也就是说”Object {}”原型对象就是原型链的终点了。

4 、普通对象与函数对象

String.prototype.trim = function() { return this.replace;};

' foo bar '.trim(); // 'foo bar'

1.4、通过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>

运行结果:

图片 29

示例中给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]");

运行结果:

图片 30

为了扩展更加方便,可以修改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();

运行结果:

图片 31

JavaScript 中,万物皆对象!但对象也是有区别的。分为普通对象和函数对象,Object ,Function 是JS自带的函数对象。下面举例说明

但是这样做又有一个缺点,因为比较新版本的浏览器中的 JavaScript 引擎在 String 对象中本身就提供了 trim 方法, 那么我们自己定义的 trim 就会覆写它自带的 trim。其实,我们在定义 trim 方法之前,可以做个简单的检测,看是否需要自己添加这个方法:

1.5、通过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);

运行结果:

图片 32

示例代码:

            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));

运行结果:

图片 33

练习:扩展Date,增加一个显示中文日期的方法,如:new Date().trandition(),输出2016年08月09日 23点15分18秒;使用prototype的方式间接调用该方法。

function f1(){}; var f2 = function(){}; var f3 = new Function('str','console.log; var o3 = new f1(); var o1 = {}; var o2 =new Object(); console.log; //function console.log; //function console.log; //object console.log; //object console.log; //object console.log; //function console.log; //function console.log; //function 
if(!String.prototype.trim) { String.prototype.trim = function() { return this.replace; };}

1.6、prototype和__proto__的区别

图片 34

prototype是函数才有的属性,prototype本身就是一个对象,prototype对象中拥有constractor构造器(该构造器反向指回函数)

__proto__是对象带有属性,用于访问创建对象的类型所对应的原型

字面量创建的对象(o={})或Object创建的对象__proto__指向Object.prototype

示例:

var a = {};
console.log(a.prototype);  //undefined
console.log(a.__proto__);  //Object {}

var b = function(){}
console.log(b.prototype);  //b {}
console.log(b.__proto__);  //function() {}

<!DOCTYPE html>
<html>

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

    <body>
        <script>
            //构造方法,类,函数
            function Foo(name) {
                this.name = name;
            }

            var f1 = new Foo("zhangguo");

            //prototype是函数才有的属性,prototype本身就是一个对象,prototype对象中拥有constractor构造器(该构造器反向指回函数)
            console.log(f1.prototype);  //undefined
            console.log(Foo.prototype);  //function Foo.prototype
            //__proto__是对象带有属性,用于访问创建对象的类型所对应的原型
            console.log(f1.__proto__);   //function Foo.prototype
            console.log(Foo.__proto__);  //function(){}
            //Foo.__proto__ 就是Function.prototype

            console.log(Foo.__proto__===Function.prototype);  //true
            console.log(Foo.prototype===f1.__proto__);  //true


            //字面量创建的对象(o={})或Object创建的对象__proto__指向Object.prototype
            var o={};
            console.log(o.__proto__===Object.prototype);  //true
            var obj=new Object();
            console.log(obj.__proto__===Object.prototype);  //true
            var obj2=Object.create({});
            console.log(obj2.__proto__===Object.prototype);  //false

            //            console.log(obj1.prototype);
            //            console.log(obj1.__proto__);
            //            console.log(Foo.prototype);
        </script>
    </body>

</html>

结果:

图片 35

在上面的例子中 o1 o2 o3 为普通对象,f1 f2 f3 为函数对象。怎么区分,其实很简单,凡是通过 new Function() 创建的对象都是函数对象,其他的都是普通对象。 f1,f2,归根结底都是通过 new Function()的方式进行创建的。Function Object 也都是通过 New Function()创建的。

原型链

1.7、__proto__属性指向

图片 36

/*1、字面量方式*/
var a = {};
console.log(a.__proto__);  //Object {}

console.log(a.__proto__ === a.constructor.prototype); //true

/*2、构造器方式*/
var A = function(){};
var a = new A();
console.log(a.__proto__); //A {}

console.log(a.__proto__ === a.constructor.prototype); //true

/*3、Object.create()方式*/
var a1 = {a:1}
var a2 = Object.create(a1);
console.log(a2.__proto__); //Object {a: 1}

console.log(a.__proto__ === a.constructor.prototype); //false(此处即为图1中的例外情况)

 示例:

<!DOCTYPE html>
<html>

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

    <body>
        <script>
            //构造方法,类,函数
            function Foo(name) {
                this.name = name;
            }

            var f1 = new Foo("zhangguo");

            console.log(f1.constructor === Foo); //true
            console.log(f1.__proto__ === Foo.prototype); //true

            /*1、字面量方式*/
            var a = {};
            //a的__proto__是指向了a的构造器所指向的原型
            //字面量创建的对象的__proto__指向了Object的原型
            console.log(a.__proto__ === a.constructor.prototype); //true

            /*2、构造器方式*/
            var A = function() {};

            var a = new A();

            console.log(a.__proto__); //A的原型

            console.log(a.__proto__ === a.constructor.prototype); //true
            console.log(a.__proto__ === A.prototype); //true

            /*3、Object.create()方式*/
            var a1 = {a: 1}
            var a2 = Object.create(a1);

            console.log(a2.__proto__===a1);   //true,a1就是a2的原型(父类)
        </script>
    </body>

</html>

结果:

图片 37

5、prototype原型

JavaScript 中定义或实例化任何一个对象的时候,它都会被附加一个名为 __proto__ 的隐藏属性,原型链正是依靠这个属性才得以形成。但是千万别直接访问 __proto__ 属性,因为有些浏览器并不支持直接访问它。另外 __proto__ 和 对象的 prototype 属性也不是一回事,它们各自有各自的用途。

1.8、原型链

图片 38

示例:

var A = function(){};
var a = new A();
console.log(a.__proto__); //A {}(即构造器function A 的原型对象)
console.log(a.__proto__.__proto__); //Object {}(即构造器function Object 的原型对象)
console.log(a.__proto__.__proto__.__proto__); //null

示例:

<!DOCTYPE html>
<html>

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

    <body>
        <script>
            //构造方法,类,函数
            function Foo(name) {
                this.name = name;
            }

            var f1 = new Foo("zhangguo");
            //1、var f1={};
            //2、f1.__proto__=Foo.prototype;
            //3、Foo.call(f1,"zhangguo");

            //在Foo的原型中添加属性age
            Foo.prototype.age = 80;
            Foo.prototype.show = function() {
                console.log("name:"   this.name   ",age:"   this.age);
            }

            var f2 = new Foo("zhangsan");

            console.log(f1.age == f2.age);
            f1.show();
            f2.show();

            var A = function() {};
            var a = new A();
            console.log(a.__proto__); //A {}(即构造器function A 的原型对象)
            console.log(a.__proto__.__proto__); //Object {}(即构造器function Object 的原型对象)
            console.log(a.__proto__.__proto__.__proto__); //null
        </script>
    </body>

</html>

结果:

图片 39

当查找一个对象的属性时,JavaScript 会向上遍历原型链,直到找到给定名称的属性为止,到查找到达原型链的顶部(也就是 Object.prototype),如果仍然没有找到指定的属性,就会返回 undefined。

function Person(name, age){ 
    this.name = name; 
    this.age = age; 
  } 
Person.prototype.MaxNumber = 9999;
Person.__proto__.MinNumber = -9999;
var will = new Person("Will", 28); 
console.log(will.MaxNumber); // 9999 
console.log(will.MinNumber); // undefined 

在这个例子中分别给”Person.prototype “和” Person.proto”这两个原型对象添加了”MaxNumber “和”MinNumber”属性,这里就需要弄清”prototype”和”proto”的区别了。

“Person.prototype “对应的就是Person构造出来所有实例的原型,也就是说”Person.prototype “属于这些实例原型链的一部分,所以当这些实例进行属性查找时候,就会引用到”Person.prototype “中的属性。

对象创建方式影响原型链

var July = { 
   name: "张三", 
   age: 28, 
   getInfo: function(){ 
     console.log(this.name   " is "   this.age   " years old"); 
   }
 } 
console.log(July.getInfo()); 

当使用这种方式创建一个对象的时候,原型链就变成下图了. July对象的原型是”Object.prototype”也就是说对象的构建方式会影响原型链的形式。

图片 40

在JavaScript 中,每当定义一个对象时候,对象中都会包含一些预定义的属性。其中函数对象的一个属性就是原型对象 prototype。注:普通对象没有prototype,但有_ proto _属性。

怎么理解呢?其实,当我们创建 myObject 函数时,实际上是创建了一个 Function 类型的对象:

1.9、构造对象

在JavaScript中,每个函数 都有一个prototype属性,当一个函数被用作构造函数来创建实例时,这个函数的prototype属性值会被作为原型赋值给所有对象实例(也就是设置 实例的`__proto__`属性),也就是说,所有实例的原型引用的是函数的prototype属性。(只有函数对象才会有这个属性!)

new 的过程分为三步  

var p = new Person('张三',20);

1. var p={}; 初始化一个对象p。

2. p._proto_=Person.prototype;,将对象p的 __proto__ 属性设置为 Person.prototype

3. Person.call(p,”张三”,20);调用构造函数Person来初始化p。

 

1、prototype是函数才有的属性

2、__proto__是每个对象都有的属性,它不是W3C的标准属性,它指向prototype

3、__proto__可以理解为“构造器原型”,__proto__===constructor.prototype(Object.create()创建的对象不适用)

4、当js引擎查找对象的属性时,先查找对象本身是否存在该属性,如果不存在会在原型链上查找,但不会查找自己的prototype

原型对象其实就是普通对象(Function.prototype除外,它是函数对象,但它很特殊,他没有prototype属性(前面说道函数对象都有prototype属性))。看下面的例子:

console.log; // function

5、函数的原型对象constructor默认指向函数本身,原型对象除了有原型属性外,为了实现继承,还有一个原型链指针__proto__,该指针指向上一层的原型对象,而上一层的原型对象的结构依然类似,这样利用__proto__一直指向Object的原型对象上,而Object的原型对象用Object.prototype.__proto__

null表示原型链的最顶端,如此变形成了javascript的原型链继承,同时也解释了为什么所有的javascript对象都具有Object的基本方法。

 

引用

 function f1(){}; console.log //f1{} console.log //Object console.log(typeof Function.prototype) // Function,这个特殊 console.log(typeof Object.prototype) // Object console.log(typeof Function.prototype.prototype) //undefined

这里要说明一下,Function 是 JavaScript 中预定义的一个对象,所以它也有自己预定义的属性和方法,当然也有 __proto__,以此实现原型链。也就是说,JavaScript 引擎内可能有类似如下的代码片段:

三、JavaScript面向对象(OOP)

从这句console.log //f1 {} 的输出就结果可以看出,f1.prototype就是f1的一个实例对象。就是在f1创建的时候,创建了一个它的实例对象并赋值给它的prototype,基本过程如下:

Function.prototype = { arguments: null, length: 0, call: function() { // secret code }, apply: function(){ // secret code }, ...};

3.1、封装

因为在JavaScript中没有访问修饰符,也没有块级作用域,this所定义的属性默认对外就是公开访问的,如下示例:

            function Animal(){
                this.name="动物";
                this.weight=0;  //重量
            }

            var cat=new Animal();
            cat.name="猫";
            cat.weight=-90;
            console.log(cat);

输出:

图片 41

weight是-90,不正确,封装:

<!DOCTYPE html>
<html>

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

    <body>
        <script type="text/javascript">
            function Animal() {
                this.name = "动物";
                var weight = 0; //重量
                this.getWeight = function() { //读 
                    return weight;
                };
                this.setWeight = function(_weight) { //写
                    if(_weight > 0) {
                        weight = _weight;
                    } else {
                        throw "动物的重量必须为正数";
                    }
                }
            }

            var cat = new Animal();
            cat.name = "猫";
            try{
                cat.setWeight(-90);
            }catch(e){
                console.log(e);
            }
            console.log(cat);
        </script>
    </body>

</html>

结果:

图片 42

            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函数本身用于封装动物的属性与行为,运行结果:

图片 43

 var temp = new f1(); f1. prototype = temp;

事实上,JavaScript 引擎代码不可能这样简单,这里只是描述一下原型链是如何工作的。

3.2、继承

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();

运行结果:

图片 44

所以,Function.prototype为什么是函数对象就迎刃而解了,上文提到凡是new Function ()产生的对象都是函数对象,所以temp1是函数对象。

我们定义了一个函数 myObject,它还有一个参数 name,但是并没有给它任何其它属性,例如 length 或者其它方法,如 call。那么下面这段代码为啥能正常执行呢?

3.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>

运行结果:

 图片 45

示例:

<!DOCTYPE html>
<html>

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

    <body>
        <script type="text/javascript">
            //二种重写的办法
            function Animal(name) {
                this.name = name || "动物";
                this.bark = function() {
                    console.log(this.name   "叫了:嗷嗷...");
                }
            };

            function Cat() {
                this.hunt = function() {
                    console.log(this.name   "正在捕老鼠!");
                }
            }
            Cat.prototype = new Animal("猫");
            Cat.prototype.bark = function() {
                console.log("喵喵");
            }
            var cat = new Cat();
            cat.hunt();
            cat.bark();

            function Dog() {
                this.hunt = function() {
                    console.log(this.name   "正在捕猎!");
                }
                this.bark = function() {
                    console.log("汪汪");
                }
            }
            //让Dog继承Animal,修改原型的指向
            Dog.prototype = new Animal("狗");
            var dog = new Dog();
            dog.hunt();
            dog.bark();

            //重载的办法
            function add(n) {
                if(typeof n === "number") {
                    return   n;
                }
                if(n instanceof Array){
                    var sum=0;
                    for(var i=0;i<n.length;i  ){
                        sum =n[i];
                    }
                    return sum;
                }
            }
            console.log(add(100));
            console.log(add([1, 2, 3, 4, 5]));
        </script>
    </body>

</html>

结果:

图片 46

 

练习:请使用javascript完成一个简单工厂设计模式。

var temp1 = new Function (); Function.prototype = temp1;
console.log; // 结果:1,是参数的个数

四、示例下载

那原型对象是用来做什么的呢?主要作用是用于继承。举了例子:

这是因为我们定义 myObject 时,同时也给它定义了一个 __proto__ 属性,并赋值为 Function.prototype,所以我们能够像访问其它属性一样访问 myObject.length,即使我们并没有定义这个属性,因为它会顺着 __proto__ 原型链往上去找 length,最终在 Function 里面找到了。

五、视频

var person = function{ this.name = name};person.prototype.getName = function(){ return this.name; // 这里this指向原型对象person ==>person.name}var xpg = new person;xpg.getName(); //xiaopingguo

那为什么找到的 length 属性的值是 1,而不是 0 呢,是什么时候给它赋值的呢?由于 myObject 是 Function 的一个实例:

从这个例子可以看出,通过给person.prototype设置了一个函数对象的属性,那有person实例出来的普通对象就继承了这个属性。具体是怎么实现的继承,就要讲到下面的原型链了。

console.log(myObject instanceof Function); // trueconsole.log(myObject === Function); // false

在深入的讲一遍:无论什么时候,只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性,默认情况下prototype属性会默认获得一个constructor属性,这个属性是一个指向prototype属性所在函数的指针,有些绕了啊,写代码、上图!

当实例化一个对象的时候,对象的 __proto__ 属性会被赋值为其构造者的原型对象,在本示例中就是 Function,此时构造器回去计算参数的个数,改变 length 的值。

function Person(){}
console.log(myObject.__proto__ === Function.prototype); // true

根据上图可以看出Person对象会自动获得prototyp属性,而prototype也是一个对象,会自动获得一个constructor属性,该属性正是指向Person对象。

而当我们用 new 关键字创建一个新的实例时,新对象的 __proto__ 将会被赋值为 myObject.prototype,因为现在的构造函数为 myObject,而非 Function。

当调用构造函数创建一个实例的时候,实例内部将包含一个内部指针(很多浏览器这个指针名字为_ proto _ )指向构造函数的prototype,这个连接存在于实例和构造函数的prototype之间,而不是实例与构造函数之间。

var myInstance = new myObject;console.log(myInstance.__proto__ === myObject.prototype); // true
function Person{ this.name=name;}Person.prototype.printName=function;}var person1=new Person;var person2=new Person;

新对象除了能访问 Function.prototype 中继承下来的 call 和 apply 外,还能访问从 myObject 中继承下来的 getName 方法:

Person的实例person1中包含了name属性,同时自动生成一个_ proto _属性,该属性指向Person的prototype,可以访问到prototype内定义的printName方法,大概就是这个样子的:

console.log; // foo var mySecondInstance = new myObject; console.log(mySecondInstance.getName; // barconsole.log; // foo

写段程序测试一下看看prototype内属性、方法是能够共享

其实这相当于把原型对象当做一个蓝本,然后可以根据这个蓝本创建 N 个新的对象。

function Person{ this.name=name;}Person.prototype.share=[];Person.prototype.printName=function;}var person1=new Person;var person2=new Person;person1.share.push;person2.share.push;console.log; //[1,2]

再看一个多重prototype链的例子:

果不其然!实际上当代码读取某个对象的某个属性的时候,都会执行一遍搜索,目标是具有给定名字的属性,搜索首先从对象实例开始,如果在实例中找到该属性则返回,如果没有则查找prototype,如果还是没有找到则继续递归prototype的prototype对象,直到找到为止,如果递归到object仍然没有则返回错误。同样道理如果在实例中定义如prototype同名的属性或函数,则会覆盖prototype的属性或函数。—-这就是Javascript的原型链。

// 多重prototype链的例子function Employee{this.name = "";this.dept = "general";this.gender = "unknown";}function WorkerBee(){this.projects = [];this.hasCar = false;}WorkerBee.prototype = new Employee; // 第一层prototype链function Engineer(){this.dept = "engineer"; //覆盖了 "父对象"this.language = "javascript";}Engineer.prototype = new WorkerBee; // 第二层prototype链var jay = new Engineer{alert; //engineer, 找到的是自己的属性alert; // false, 搜索到的是自己上一层的属性alert; // unknown, 搜索到的是自己上二层的属性}
function Person{ this.name=name;}Person.prototype.share=[];var person=new Person;person.share=0;console.log; //0;而不是prototype中的[]

上面这个示例的对象关系如下:

6.原型链

JS在创建对象的时候,都有一个叫做_ proto _的内置属性,用于指向创建它的函数对象的原型对象prototype。以上面的例子

复制代码 代码如下:console.log(xpg.__ proto __ === person.prototype) //true

同样,person.prototype对象也有_ proto _属性,它指向创建它的函数对象的prototype

复制代码 代码如下:console.log(person.prototype.__ proto __=== Object.prototype) //true

继续,Object.prototype对象也有_ proto _属性,但它比较特殊,为null

复制代码 代码如下:console.log(Object.prototype.__ proto __) //null

这个有_ proto _ 串起来的直到Object.prototype._ proto _为null的链叫做原型链。如下图:

原型链中属性查找: 当查找一个对象的属性时,JavaScript 会向上遍历原型链,直到找到给定名称的属性为止,到查找到达原型链的顶部 - 也就是 Object.prototype - 但是仍然没有找到指定的属性,就会返回 undefined,我们来看一个例子:

 function foo() { this.add = function  { return x   y; }}foo.prototype.add = function  { return x   y   10;}Object.prototype.subtract = function  { return x - y;}var f = new foo; //结果是3,而不是13alert; //结果是-1

通过代码运行,我们发现subtract是安装我们所说的向上查找来得到结果的,但是add方式有点小不同,这也是我想强调的,就是属性在查找的时候是先查找自身的属性,如果没有再查找原型,再没有,再往上走,一直插到Object的原型上,所以在某种层面上说,用 for in语句遍历属性的时候,效率也是个问题。

还有一点我们需要注意的是,我们可以赋值任何类型的对象到原型上,但是不能赋值原子类型的值,比如如下代码是无效的:

function Foo() {}Foo.prototype = 1; // 无效

7、构造函数、实例和原型对象的区别

实例就是通过构造函数创建的。实例一创造出来就具有constructor属性和proto属性,

构造函数中有一个prototype属性,这个属性是一个指针,指向它的原型对象。

原型对象内部也有一个指针指向构造函数:Person.prototype.constructor = Person;

实例可以访问原型对象上定义的属性和方法。

在这里person1和person2就是实例,prototype是他们的原型对象。

 function Animal //&#31215;&#32047;&#26500;&#36896;&#20989;&#25968; { this.name = name;//&#35774;&#32622;&#23545;&#35937;&#23646;&#24615; } Animal.prototype.behavior = function() //&#32473;&#22522;&#31867;&#26500;&#36896;&#20989;&#25968;&#30340;prototype&#28155;&#21152;behavior&#26041;&#27861; { alert("this is a " this.name); } var Dog = new Animal;//&#21019;&#24314;Dog&#23545;&#35937; var Cat = new Animal;//&#21019;&#24314;Cat&#23545;&#35937; Dog.behavior();//&#36890;&#36807;Dog&#23545;&#35937;&#30452;&#25509;&#35843;&#29992;behavior&#26041;&#27861; Cat.behavior();//output "this is a cat" alert(Dog.behavior==Cat.behavior);//output true;

8、原型的使用

原型使用方式1: 在使用原型之前,我们需要先将代码做一下小修改:

 var Calculator = function  { this.decimalDigits = decimalDigits; this.tax = tax; };

然后,通过给Calculator对象的prototype属性赋值对象字面量来设定Calculator对象的原型。

 Calculator.prototype = { add: function  { return x   y; }, subtract: function  { return x - y; } }; //alert.add;

我们就可以new Calculator对象以后,就可以调用add方法来计算结果了。

原型使用方式2: 第二种方式是,在赋值原型prototype的时候使用function立即执行的表达式来赋值,即如下格式:

复制代码 代码如下:Calculator.prototype = function ;它的好处在前面的Item里已经知道了,就是可以封装私有的function,通过return的形式暴露出简单的使用名称,以达到public/private的效果,修改后的代码如下:

Calculator.prototype = function () { add = function  { return x   y; }, subtract = function  { return x - y; } return { add: add, subtract: subtract }} ();//alert.add;

同样的方式,我们可以new Calculator对象以后调用add方法来计算结果了。

分步声明: 上述使用原型的时候,有一个限制就是一次性设置了原型对象,我们再来说一下如何分来设置原型的每个属性吧。

var BaseCalculator = function () { //为每个实例都声明一个小数位数 this.decimalDigits = 2;};//使用原型给BaseCalculator扩展BaseCalculator.prototype.add = function  { return x   y;};BaseCalculator.prototype.subtract = function  { return x - y;};

声明了一个BaseCalculator对象,构造函数里会初始化一个小数位数的属性decimalDigits,然后通过原型属性设置2个function,分别是add,当然你也可以使用前面提到的2种方式的任何一种,我们的主要目的是看如何将BaseCalculator对象设置到真正的Calculator的原型上。

var BaseCalculator = function() { this.decimalDigits = 2;};BaseCalculator.prototype = { add: function { return x   y; }, subtract: function { return x - y; }};

重写原型:

在使用第三方JS类库的时候,往往有时候他们定义的原型方法是不能满足我们的需要,但是又离不开这个类库,所以这时候我们就需要重写他们的原型中的一个或者多个属性或function,我们可以通过继续声明的同样的add代码的形式来达到覆盖重写前面的add功能,代码如下:

//覆盖前面Calculator的add() function Calculator.prototype.add = function  { return x   y   this.tax;};var calc = new Calculator();alert;

这样,我们计算得出的结果就比原来多出了一个tax的值,但是有一点需要注意:那就是重写的代码需要放在最后,这样才能覆盖前面的代码。

9、hasOwnProperty函数

hasOwnProperty是Object.prototype的一个方法,它可是个好东西,他能判断一个对象是否包含自定义属性而不是原型链上的属性,因为hasOwnProperty 是 JavaScript 中唯一一个处理属性但是不查找原型链的函数。

// 修改Object.prototypeObject.prototype.bar = 1; var foo = {goo: undefined};foo.bar; // 1'bar' in foo; // truefoo.hasOwnProperty; // falsefoo.hasOwnProperty; // true

只有 hasOwnProperty 可以给出正确和期望的结果,这在遍历对象的属性时会很有用。 没有其它方法可以用来排除原型链上的属性,而不是定义在对象自身上的属性。

但有个恶心的地方是:JavaScript 不会保护 hasOwnProperty 被非法占用,因此如果一个对象碰巧存在这个属性,就需要使用外部的 hasOwnProperty 函数来获取正确的结果。

var foo = { hasOwnProperty: function() { return false; }, bar: 'Here be dragons'};foo.hasOwnProperty; // 总是返回 false// 使用{}对象的 hasOwnProperty,并将其上下为设置为foo{}.hasOwnProperty.call; // true

当检查对象上某个属性是否存在时,hasOwnProperty 是唯一可用的方法。同时在使用 for in loop 遍历对象时,推荐总是使用 hasOwnProperty 方法,这将会避免原型对象扩展带来的干扰,我们来看一下例子:

// 修改 Object.prototypeObject.prototype.bar = 1;var foo = {moo: 2};for { console.log; // 输出两个属性:bar 和 moo}

我们没办法改变for in语句的行为,所以想过滤结果就只能使用hasOwnProperty 方法,代码如下:

// foo 变量是上例中的for { if ) { console.log; //moo }}

这个版本的代码是唯一正确的写法。由于我们使用了 hasOwnProperty,所以这次只输出 moo。如果不使用 hasOwnProperty,则这段代码在原生对象原型被扩展时可能会出错。

总结:推荐使用 hasOwnProperty,不要对代码运行的环境做任何假设,不要假设原生对象是否已经被扩展了

10、拓展

_ ptoto _属性

_ ptoto _属性是实例指向原型对象的一个指针,它的作用就是指向构造函数的原型属性constructor,通过这两个属性,就可以访问原型里的属性和方法了。

Javascript中的对象实例本质上是由一系列的属性组成的,在这些属性中,有一个内部的不可见的特殊属性——_ proto _,该属性的值指向该对象实例的原型,一个对象实例只拥有一个唯一的原型。

function Box(){ //大写,代表构造函数 Box.prototype.name = "trigkit4";//原型属性 Box.prototype.age = "21"; Box.prototype.run = function()//原型方法 { return this.name   this.age   'studying'; }}var box1 = new Box();var box2 = new Box();alert;//构造属性,可以获取构造函数本身, //作用是被原型指针定位,然后得到构造函数本身

_ proto _属性和prototype属性的区别

prototype是原型对象中专有的属性。 _ proto _ 是普通对象的隐式属性,在new的时候,会指向prototype所指的对象; _ ptoto _ 实际上是某个实体对象的属性,而prototype则是属于构造函数的属性。_ ptoto _只能在学习或调试的环境下使用。

原型模式的执行流程

1.先查找构造函数实例里的属性或方法,如果有,就立即返回。 2.如果构造函数的实例没有,就去它的原型对象里找,如果有,就立即返回

function Box(){ //大写,代表构造函数 Box.prototype.name = "trigkit4";//原型属性 Box.prototype.age = "21"; Box.prototype.run = function()//原型方法 { return this.name   this.age   'studying'; }}var box1 = new Box;//trigkit4,原型里的值box1.name = "Lee";alert;//Lee,就进原则var box2 = new Box;//trigkit4,原型的值,没有被box1修改

function Box(){ this.name = "Bill";}Box.prototype.name = "trigkit4";//原型属性Box.prototype.age = "21";Box.prototype.run = function()//原型方法{ return this.name   this.age   'studying';}var box1 = new Box;//Bill,原型里的值box1.name = "Lee";alert;//Lee,就进原则

function Person(){};Person.prototype.name = "trigkit4";Person.prototype.say = function(){ alert;}var p1 = new Person();//prototype是p1和p2的原型对象var p2 = new Person();//p2为实例化对象,其内部有一个__proto__属性,指向Person的prototypeconsole.log;//undefined,这个属性是一个对象,访问不到console.log;//Personconsole.log(Person.prototype.constructor);//原型对象内部也有一个指针指向构造函数console.log;//这个属性是一个指针指向prototype原型对象p1.say();//实例可以访问到在原型对象上定义的属性和方法

构造函数.prototype = 原型对象 原型对象.constructor = 构造函数

- 原型对象.isPrototypeof 判断实例对象的原型是不是当前对象。

以上就是本文的全部内容,希望对大家的学习有所帮助。

本文由pc28.am发布于pc28.am,转载请注明出处:JavaScript学习总括

上一篇:如何在IIS景况下安插Rewrite法规,js点击文本框弹 下一篇:没有了
猜你喜欢
热门排行
精彩图文