面向对象之原型——challenge

面向对象之原型

object-oriented面向对象的设计,不同于其他语言,js中的面向对象没有类的概念,因此,其对象也有些特殊。

所谓对象就是无序属性的集合,其属性可以包含基本值、对象、函数。也就是说对象是一组没有特定顺序的值的集合;对象的每个属性或方法都有自己名字,名字映射到一个值(值可以是数据或者函数)

每个对象是基于引用类型创建的,是某个引用类型的实例。新对象就是new操作符后加一个构造函数创建的


1理解对象

这个就没什么必要,对象就是无序属性的集合,OK了

2创建对象

原始方式:传统的方式,只能单次创建,不能重复利用,代码重复率太高,效率低

工厂模式:抽象了具体创建对象的过程,用函数来封装以特定接口创建对象的细节;虽然解决创建多个相似对象的问题,但是没有解决对象识别即怎么知道一个对象的类型问题

构造函数:构造函数可以创建特定类型的对象比如object,array等等,也可以创建自定义的构造函数,从而定义自定义对象的类型和方法

         //普通的创建    只能单次创建,不能重复使用,大量重复代码,不合理
         var person1={
               name:"double",
               age:24,
               sex:"man"
         }
         console.log(person1.name)         //double

         //工厂模式   抽取了创建具体对象的过程
         function createPerson(name,age,sex){
               var obj=new Object();
               obj.name=name;
               obj.age=age;
               obj.sex=sex;
               obj.sayName=function(){
                      console.log(this.name)
               }
               return obj;                  //工厂模式下必须有return
         }

         var person1=createPerson("double",23,"man")
         var person2=createPerson("single",34,"woman")

         console.log(person1.name)       //double

          //构造函数模式  new+构造函数
         function Person(name,age,sex){
                this.name=name;
                this.age=age;
                this.sex=sex;
                this.sayName=function(){
                      console.log(this.name);
                }                //return this       可省略
         }
         var person1=new Person("double",34,"man")
         var person2=new Person("single",32,"woman")
         console.log(person1.name)    //double

构造函数创建对象的不同

1、没有显示的创建对象  2、直接将属性和方法赋给this对象  3、没有return语句

注意:构造函数应该以大写字符开头,普通函数以小写字符开头。

创建对象的过程

1、创建一个新对象;2、将构造函数的作用域赋予给新对象;3、执行构造函数中的代码;4、返回新对象

对象的constructor属性,指向对象的构造函数

         console.log(person1.constructor == Person)  //true  //对象都有一个constructor(构造函数)属性,true说明是指向Person
         console.log(person2.constructor == Person)

         //检测类型instanceof   创建的对象既是Object又是Person的实例
         console.log(person1 instanceof Object)          //true
         console.log(person1 instanceof Person)
         console.log(person2 instanceof Object)
         console.log(person2 instanceof Person)

关于构造函数的补充

构造函数与函数的区别在于调用的方式不同。任何函数只要通过new操作符调用就是构造函数

         构造函数与普通函数

         //构造函数
        var person3=new Person("nothing",35,"woman")
        console.log(person3.age)   //35

        //普通函数
        Person("world",34,"man")
        console.log(window.age)    //34      直接调用,那就跑到全局作用域上,添加到window

        //在另一个对象的作用域调用
        var  obj=new Object()
        Person.call(obj,"double",45,"man")
        console.log(obj.name)       //double

构造函数的问题

每个方法都要在每个实例上重新创建一遍;js中函数本身就是对象,所以每个实例上的每个新的方法,就是新的对象;不同实例上的同名函数是不相等的

        function Person(name,age,sex){
              this.name=name;
              this.age=age;
              this.sex=sex;
              this.sayName=function(){
                      return this.name
              }
        }

        var person1=new Person("double",34,"man")
        var person2=new Person("single",45,"woman")

        console.log(person1.sayName==person2.sayName)    //false   不是相同的函数,而是分别创建了
        console.log(person1.name.prototype==person2.name.prototype)       //true   指向同一原型属性
        console.log(person1.sayName.prototype==person2.sayName.prototype)   //false  指向不同原型属性

        //创建两个完成相同任务的Function实例确实没必要,况且有Function存在,不需要将函数绑定到特定对象上去
        function Person(name,age,sex){
              this.name=name;
              this.age=age;
              this.sex=sex;
              this.sayName=sayName;
        }

        function sayName(){      //提出来是可以的,person1和person2指向全局作用域上的定义的同一函数,一旦函数的方法多了就麻烦啦
            return this.name
        }
        //一句话结束:原先是创建对象的方法来创建函数,每个person中都包含不同Function实例,会导致不同的作用域链和标识符解析;现在是声明函数的方法创建函数

原型模式

每个引用类型(Array Object Function)都有prototype(原型)属性,是个指针,指向一个对象,这个对象就是包含可以由特定类型的所有实例共享的属性和方法(即原型对象);

所有的引用类型都在其构造函数的原型上定义了方法,Array.prototype可以找到sort方法,在String.prototype可以找到substring方法;

好处:让所有对象实例共享它包含的属性和方法

        function Person(){
        }

        Person.prototype.name="double"
        Person.prototype.age=23
        Person.prototype.sex="man"
        Person.prototype.sayName=function(){
              console.log(this.name)
        }

        var person1=new Person()
        person1.sayName()         //double

        var person2=new Person()
        person2.sayName()         //double

        console.log(person1.sayName==person2.sayName)   //true

1、理解原型模式

prototype属性:每一个新创建的函数,都有一个prototype属性,说过了,是个指针,指向它的原型对象(Person.prototype);

constructor属性:所有的原型对象有个constructor属性,也是个指针,指向prototype所在的函数。即Person.prototype.constructor指向的就是Person这个构造函数。

_proto_属性:创建构造函数后,其原型对象默认只会取得constructor属性,其他方法会由object继承。当调用构造函数创建一个新实例后,该实例内部有个_proto_属性,也是指针,指向原型对象

注意:这个属性对脚本是不可见的,称为隐式属性;这个连接存在于实例和原型对象之间,不是存在实例与构造函数之间,来看此图就知道了

isPrototypeOf()方法:这是原型对象的一个方法,用来判断某个实例与原型对象是否存在_proto_联系

   console.log(Person.prototype.isPrototypeOf(person1))     //true
   console.log(Person.prototype.isPrototypeOf(person2))     //true

Object.getPrototypeOf()方法:ES5新增的方法,返回原型对象

        console.log(Object.getPrototypeOf(person1) == Person.prototype)    //true
        console.log(Object.getPrototypeOf(person1).name)         //double   

注意:每当代码读取某个对象的某个属性,都会执行一次搜索。首先搜索该实例,没有就搜索原型对象,再没有就搜索Object原型对象;一旦找到就返回,不会继续搜索,也就是说相同名字的属性就会被遮蔽,而不是替代或者消失,是遮蔽。当删除属性时就会继续查找

hasOwnePrototype()方法:用来检测一个属性是存在实例中还是原型中(属性存在实例中返回true)

        console.log(person1.hasOwnProperty("name"))      //false    根据上面的则是在原型中的
        console.log(person1.hasOwnProperty("age"))       //false

2、原型和in操作符

in操作符可以用两种方式,一个是单独使用,一个是for in 循环中,来吧

单独使用:通过对象能够访问特定属性,不管存在于实例还是原型中都返回true

        function Person(){
        }

        Person.prototype.name="double"
        Person.prototype.age=23
        Person.prototype.sex="man"

        var person1=new Person()
        var person2=new Person()

        console.log(person1.hasOwnProperty("name"))     //false    存在原型中
        console.log("name" in person1)                  //true

        person1.name="single"
        console.log(person1.hasOwnProperty("name"))     //true     存在实例中,这里实例的name只是遮盖了原型中的name
        console.log("name" in person1)                  //true
                //可以通过Object.hasOwnProperty()和操作符in来判断到底是存在于实例还是原型中
        function hasPrototypeProperty(object,name){
               return !object.hasOwnProperty(name)&&(name in object)
        }

        console.log(hasPrototypeProperty(person1,"name"))  //false    说明存在实例中
        console.log(hasPrototypeProperty(person2,"name"))  //true     说明存在原型中

for in 使用:返回的是所有能够通过对象访问的,可枚举的(enmuerated)属性,既包括实例中又包括原型中,遮蔽的原型中(不可枚举的)的实例属性也会被循环返回;但是这不是我们想要的结果,我们只想要实例中可枚举的属性,可以用ES5中Object.keys()的方法:接受一个对象作为参数,返回一个包含所有可枚举属性的字符串数组

        function Person(){
        }

        Person.prototype.name="double"
        Person.prototype.age=23
        Person.prototype.sex="man"
        Person.prototype.sayName=function(){
               return this.name
        }

        var person1=new Person()
        var person2=new Person()

        var keys=Object.keys(Person.prototype)
        console.log(keys)              //返回的是字符串数组,所有可枚举的属性["name","age","sex","sayName"]

        var keyss=Object.getOwnPropertyNames(Person.prototype)
        console.log(keyss)             //返回的是所有实例属性,含有不可枚举的属性["constructor","name","age","sex","sayName"]

所以呢,直接用字面量原型吧,简洁明,大方得体;注意了:因为将原型设置为字面量形式,所以呢,它现在变为一个新对象了,结果是相同的,但是呢,这里的constructor不再指向Person了

为啥呢?

每创建一个对象,此对象就会有他的原型对象(原型对象会自动得到constructor),这里重写了原型对象,所以呢constructor就变成新原型对象的constructor属性,指向的是Object构造函数

        function Person(){
        }

        Person.prototype={                     字面量表示               constructor:Person,        当constructor very important时,那就作为返回值带上呗
               name : "double",
               age  :  12,
               sex  : "woman",
               sayName : function(){
                       return this.name
               }
        }

        var person1=new Person()
        console.log(person1.name)    //double

3、原型的动态性

正如前面所说的,当查找某一个属性的时候,会进行层层的搜索,首先在实例层中,然后在原型层,再次在Object中。

注意:原型的查找是一次搜索的,对原型的任意修改都会立即从实例上反映出来(即使是先创建实例后修改原型)

        var person1=new Person()
        Person.prototype.sayName=function(){
              alert(this.age)
        }

        person1.sayName()   //12

所以呢,可以通过对原型添加、修改某些属性达到效果,但是当实例创建了,重写原型gg了,因为实例和原型间的_prototy_是个指针,不是副本,重写原型就切断了原先的联系,会报错的。

基于前面都是自定义对象,那么对于原生的引用类型呢,是不是可以对原型自定义的添加属性呢?

当然可以啦,但是不推荐做,原因嘛,你懂的。

        //为String原型添加个方法
        String.prototype.startWidth=function(text){
               return this.indexOf(text)==0
        }
        var message="Hello world"
        console.log(message.startWidth("Hello"))    //true

4、原型模式的问题

从上面可以很容易的看出来,共享性是原型模式的最大特色,但是它是一把双刃剑,对于函数方法十分有效,游刃有余;可是对于一些基本属性,就没办法相构造函数那样解决(当然我们可以在后续添加,遮盖原型中的属性);最重要的是对于包含引用类型值得属性就是个大麻烦,比如数组,来看例子吧!

       function Person(){
       }
       Person.prototype={
               constructor:Person,
               name:"double",
               age:23,
               sex:"woman",
               friends:["single","mike"]
       }
       var person1=new Person()
       var person2=new Person()

       person1.friends.push("tom","jack")

       console.log(person1.friends==person2.friends)   //true   因为被共享了  

所以各位,这种原型模式你们会用吗?

那么有什么好的办法呢?当然是有的,来瞧一瞧看一看,组合使用构造函数模式和原型模式就可以了(也是使用的最多的)

       function Person(name,age,sex){
               this.name=name;
               this.age=age;
               this.sex=sex;
       }
       Person.prototype={
              constructor:Person,
              sayName:function(){
                      console.log(this.name)
              }
       }

       var person1=new Person("double",23,"man")
       var person2=new Person("single",34,"woman")

       console.lgo(person11.name===person2.name)       //false 基本值不一样
       console.log(person1.sayName===person2.sayName)  //true  方法一样

有些奇怪吗?确实,为啥好端端的函数时,将原型和构造函数分开,来吧,让封装函数让你享受封装的魅力所在。我们称之为动态原型模式(也就是比较完美了)

       function Person(name,age,sex){
               this.name=name;
               this.age=age;
               this.sex=sex;

               if(typeof this.sayName != "function"){                  //当sayName方法不存在时,添加到原型中,初次调用时执行,此后原型已经初始化了,后续更改也会在实例中实时反映的
                    Person.prototype.sayName=function(){
                           alert(this.name)
                    };
               }
       }

       var friends=new Person("double",34,"man")
       friends.sayName()      //double

于此,原型模式讲的也就差不多了,补充一下,事实上还有原型链中还有Object,所有函数(构造和普通函数)默认的原型都是Object的实例,所以呢,默认原型都会包含一个内部指针_proto_,指向Object.prototype

所以呢,自定义类型都会继承toString,valueOf默认方法的根本原因。结合上面,看一下这个原型链的图吧

还有两种模式,不常用,了解一下吧

       寄生构造函数模式:函数仅仅是封装创建对象的代码,然后返回新创建的对象
       返回对象与构造函数与构造函数的原型属性没关系,说白了,就是个普通的新对象
       在特殊的情况下为对象创建构造函数
       function Person(name,age,sex){
               var o=new Object();
               o.name=name;
               o.age=age;
               o.sex=sex;
               o.sayName=function(){
                     alert(this.name)
               };
               return o
       }
       var person=new Person("double",34,"man")    //只比工厂模式多个new
       person.sayName()

       稳妥构造函数模式
       没有公共属性,方法也不引用this对象
       在一些安全的环境下(禁用this和new)或者在防止数据被其他应用程序改动时使用
       function Person(name,age,sex){
               var o=new Object()
               o.sayName=function(){
                   alert(name)
               }
            return o
       }
       var friend=Person("double",34,"man")
       friend.sayName()                     //true

好吧,写了这么多,关注一下呗,后续再更继承的知识

如果你关注我

我就……(你懂的)

原文地址:https://www.cnblogs.com/iDouble/p/8401058.html

时间: 2024-11-05 18:43:56

面向对象之原型——challenge的相关文章

JS面向对象与原型

面向对象与原型一.创建对象 1.基本方法 1 var box = new Object(); //创建对象 2 box.name = 'Lee'; //添加属性 3 box.age = 100; 4 box.run = function(){ //添加方法 5 return this.name + this.age + '运行中...'; //this表示当前作用于下的对象 6 }; 7 8 alert(box.run()); 9 10 alert(this.anme); //这里的this代表

面向对象与原型4---原型

原型模式创建对象也有自己的缺点,它省略了构造函数传参初始化这一过程,带来的缺点就是初始化的值都是一致的.而原型最大的缺点就是它最大的优点,那就是共享.原型中所有属性是被很多实例共享的,共享对于函数非常合适,对于包含基本值的属性也还可以.但如果属性包含引用类型,就存在一定的问题: 1. //原型的缺点 function Box() {};Box.prototype = {constructor : Box,name : 'Lee',age : 100,family : ['父亲', '母亲', '

面向对象与原型5---继承

1.用原型链实现继承 最普通 但是没有办法传参,没有办法共享方法 继承是面向对象中一个比较核心的概念.其他正统面向对象语言都会用两种方式实现继承:一个是接口实现,一个是继承.而 ECMAScript 只支持继承,不支持接口实现,而实现继承的方式依靠原型链完成. 原型链是由原型加对象构造之间的关系结构形成的像一个链条一样 在 JavaScript 里,被继承的函数称为超类型(父类,基类也行,其他语言叫法),继承的函数称为子类型(子类,派生类). //继承,通过原型链实现 function Box(

面向对象与原型2---原型

1. 我们创建的每个函数(或称构造函数或对象)都有一个 prototype(原型)属性,这个属性是一个对象(这个对象下有个prototype属性,而这个属性其实是另外一个对象的引用,这个属性就是一个对象),它的用途是 包含可以由特定类型的所有实例共享的属性和方法(prototype共享放到其中的属性和方法,无论实例化多少对象,属性和方法都是共享的.这样有好处也有坏处.).逻辑上可以这么理解:prototype 通过调用构造函数而创建的那个对象的原型对象.使用原型的好处可以让所有对象实例共享它所包

第一百零九节,JavaScript面向对象与原型

JavaScript面向对象与原型 学习要点: 1.学习条件 2.创建对象 3.原型 4.继承 ECMAScript有两种开发模式:1.函数式(过程化),2.面向对象(OOP).面向对象的语言有一个标志,那就是类的概念,而通过类可以创建任意多个具有相同属性和方法的对象.但是,ECMAScript没有类的概念,因此它的对象也与基于类的语言中的对象有所不同.   一.学习条件 在JavaScript视频课程第一节课,就已经声明过,JavaScript课程需要大量的基础.这里,我们再详细探讨一下: 1

【JavaScript】——剖析面向对象与原型(一)

在面向对象与原型一章中,感觉还是挺有意思的,而且视频讲解的也很清楚,在这里总结归纳一下视频里 讲的内容,顺便整理整理自己的思路. 首先画一张图,理一下这一整章的知识点. 下面针对具体的知识点去剖析各个知识点间的关系: 工厂模式 工厂模式的引出,是为了避免在创建对象过程中产生大量重复的问题.它封装了在创建对象过程一些重复 的代码,避免了代码冗余.举个简单的Demo: 1.普通的创建对象: var p=new Object(); p.name='王虹芸'; alert('姓名:'+p.name);

十一、面向对象与原型

十一.面向对象与原型 ECMAScript有两种开发模式:1.函数式(过程化),2.面向对象(OOP).面向对象的语言有一个标志,那就是类的概念,而通过类可以创建任意多个具有相同属性和方法的对象.但是,ECMAScript没有类的概念,因此它的对象也与基于类的语言中的对象有所不同.   1.学习条件 在JavaScript视频课程第一节课,就已经声明过,JavaScript课程需要大量的基础.这里,我们再详细探讨一下: 1.xhtml基础:JavaScript方方面面需要用到. 2.扣代码基础:

js面向对象及原型(javaScript高级程序设计第3版)

一.创建对象 创建一个对象,然后给这个对象新建属性和方法. var box = new Object(); //创建一个Object对象 box.name = 'Lee'; //创建一个name属性并赋值 box.age = 100; //创建一个age属性并赋值 box.run = function () { //创建一个run()方法并返回值 return this.name + this.age + '运行中...'; }; alert(box.run()); //输出属性和方法的值 上面

javascript的基础知识及面向对象和原型属性

自己总结一下javascript的基础知识,希望对大家有用,也希望大家来拍砖,毕竟是个人的理解啊 1.1 类型检查:typeof(验证数据类型是:string) var num = 123; console.log(typeof num); // 1.2 in 运算符 作用:判断指定属性是否存在于指定的对象中. 如果指定的属性存在于指定的对象中,则 in 运算符会返回 true. 语法: 属性 in 对象 返回值:true 或者 false 示例: var obj = { age: 18 };