在Javascript中,万物皆对象,但对象也有区别,大致可以分为两类,即:普通对象和函数对象。

五.Prototype

当我们创建一个函数时:
var Person = new Object()
Person 是 Object 的实例,所以 Person 继承了Object
的原型对象Object.prototype上所有的方法:
图片 1

1、优雅的创建对象;

由此来看,你应该可以理解:prototype 产生的属性和方法是所有实例共享的。

二.构造函数
function Person(name, age, job){
    this.name = name;
    this.age = age;
    this.sayName = function(){
        alert(this.name);
    }
}
var person1 = new Person('mike',15);
var person2 = new Person('nike',18);

上例中person1和person2为Person的实例,两个实例中都有隐含的
==constructor==属性(通过原型对象继承得到),该属性指向构造函数Person

2、工厂模式

这种工厂模式并不是设计模式中的工厂方法,代码如下:

    function createObj(name, age) {
        var o = new Object();
        o.name = name;
        o.age = age;
        o.sayHello= function() {
            alert("Hello Word!");
        }
        return o;
    }

    var obj = createBlog("Tom", "88");

这种方式好多了,但是也存在一个问题,就是没办法知道一个对象是什么类型;console.log(typeof
obj);//object

Person 是 Function 类型的对象,new
之后,会继续产生一个对象,但这个新产生的对象,由于在函数中传递进去参数,并赋值给了
this 指针,那么传递进去的内容,就变成了新产生对象的属性或方法。

补充:更简单的原型语法
function Person(){}
Person.prototype = {
    name:'',
    age:'',
    sayName:function(){
    }
}

==注意==:该方法的原型对象的constructor属性不再指向Person,此时例中指向Object(新new的一个对象),如若需要可在Person.pertotype的字面量中添加:==constructor:Person,==

先看js 之前创建对象的方式存在的问题;

所以需要使用 delete 操作符,完全删除实例属性,从而可以重新访问原型。

六.Object.getPrototypeOf(obj)

获取对象的原型

为什么会有原型这个概念;

function createPerson{ var o = new Object(); o.name = name; o.age = age; o.job = job; o.sayName = function; }; return o;}var person1 = createPerson("jiangshui","22","engineer");
三.原型对象
  • 我们创建的每个函数都有一个prototype(原型)属性,指向函数的原型对象。换句话说,prototype就是通过调用构造函数而创建的那个对象实例的原型对象,使用原型对象即可以让所有对象实例共享它所包含的属性和方法,可类比于父子类(继承)。
  • 创建新函数时,其prototype属性指向的原型对象会自动获得一个constructor(构造函数)属性,该属性指向包含prototype的函数,也就是构造函数

function Person(name, age, job){
    Person.prototpe.name = father;
    Person.prototpe. = 28;
    Person.prototpe.sayName = function(){
        alert(this.name);
    }
}
var person1 = new Person();
person1.sayName();//'mike'
var person2 = new Person();
person2.sayName();//'nike'

person1.sayName == person2.sayName;//true

person1.name = 'joy';
person1.sayName();//'joy'

通过为构造函数Person的原型对象创建属性,person1,person2将继承Person.prototype的属性,如果实例定义了与原型对象同名属性,原型对象属性则会被覆盖。

原生对象的模型

JS 原生的引用类型,都是采用这种模式创建的。如
在Array.prototype.sort(),可以找到sort()方法。

原型模式也不是万能的,原型中的所有属性和方法是被所有实例共享的,所以对于函数之类非常合适,而对于包含引用类型的属性来说,就会产生一些冲突。例如:

四.__proto__
  • JS
    在创建对象(不论是普通对象还是函数对象)的时候,都有一个叫做__proto__
    的内置属性,用于指向创建它的构造函数的原型对象。

  • 普通对象的__proto__指向这个对象(this)的构造函数的prototype;

  • 函数对象的__proto__全部都是指向Function的prototype。

对象 person1 有一个 __proto__属性,创建它的构造函数是
Person,构造函数的原型对象是 Person.prototype ,所以:
==person1.__proto__ == Person.prototype==

不过,要明确的真正重要的一点就是,这个连接存在于实例==person1==与构造函数==Person==的原型对象==Person.prototype==之间,而不是存在于实例person1与构造函数Person之间。

js高级程序设计图
图片 2

Prototype对象

在默认情况下所有原型对象都会自动获得一个属性Constructor(构造函数属性)这个属性包含一个指向prototype属性所在的函数指针;

function f(){}f.prototype.foo = "abc";console.log; //undefined
一. 普通对象与函数对象
  • JavaScript
    中,万物皆对象!但对象也是有区别的。分为普通对象和函数对象,Object
    、Function 是 JS 自带的函数对象。下面举例说明

var o1 = {};
var o2 = new Object();
var o3 = new f1();

function f1(){};
var f2 = function(){};
var f3 = new Function('str');

typeof Object;//function
typeof Function;//function

typeof f1;//function
typeof f2;//function
typeof f3;//function

typeof o1;//object
typeof o2;//object
typeof o3;//object

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

实例、构造函数、原型对象关系

创建的每个实例都会有一个内部属性_proto_ 指向构造函数的原型对象;

构造函数存在prototype 属性指向该函数的原型对象;

上面person代码如图:

图片 3

图画的不好下次试试思维导图这个工具;

注意实例只跟原型对象有关系;

但是不推荐使用这个方法,如果用的太多,代码太多,会导致维护困难、代码混乱等情况。一般情况下,会先继承某个原生引用类型,然后再在新自定义的类型上创建。关于继承,后面会再总结。

原型与原型链

原型模式的问题

当我们把引用类型属性添加到原型对象时,会造成数据共享的问题,如 在person
中把一个数组放到原型对象中,此时所有的对象都会共享这个数组:

friedns:[“tom”,”Join”,”Elie”];

当我们其中一个实例对这个对象进行push(“Tem”);

所有的实例都可以访问到”Tem”;

使用 isPrototypeOf() 可以确定对象之间的关系。例如:

总结
  • 原型和原型链是JS实现继承的一种模型。
  • 原型链的形成是真正是靠__proto__ 而非prototype
  • 对象通过__proto__属性访问原型,构造函数通过prototype属性访问原型

1、字面量

var obj={name:”join”,age:18}

var obj1={name:”Tom”,age:88}

当我们需要创建多个属性只有值不一样的对象时,这种方式就悲剧来了,会造成大量的冗余代码,最重要的是代码不能复用;

现在我们知道,obj中__proto__保存的是f的prototype,
那么f的prototype中的__proto__中保存的是什么呢? 看下图:

更简单的原型语法

当我们要添加多个方法时,没必要每次都写类似这样的代码
Person.Prototype.XXX=function(){//doing something}

Person.Prototype={

constructor:Person,//注意这里要重写

sayAge:function(){ alert(this.age);},

sayJob:function(){ alert(this.job);}

}

这里为什么要重写constructor呢,很简单每创建一个函数,就会同时创建它的prototype对象(原型对象),这个对象也会获得constructor属性,

而上面我们重写了默认的prototype对象,因此constructor这时并没有指向Person,而是指向新object构造函数,所以这里要重写constructor让他指向Person;

一般而言,通过new Function产生的对象是函数对象,其他对象都是普通对象。

3、构造函数模式

function Person(name,age,job){
    this.name=name;
    this.age=age;
    this.job=job;
    this.sayNmae=function(){
        alert(this.name);
    }
}
var persong1=new Person("Join",18,"农民");
var persong2=new Person("Join",18,"地主");

构造函数模式看起来oop(面向对象)多了,上面两种模式存在的问题在构造模式中都不存在了(创建对象的代码可以复用、对象类型可以识别)看起来世界挺美好,遗憾的是

这种还是存在一个问题;

1、每一个方法都要在每个实例上重新创建一次(问题不大);

2、从上面例子可以看出每个person 实例都包含一个不同的function
实例;不要忘了ECMAScript中的函数也是对象,所以上面的代码与下面的代码等价(主要会造成不同的作用域和解析问题)

    function Person(name,age,job){
        this.name=name;
        this.age=age;
        this.job=job;
        this.sayNmae=new function(){
            alert(this.name);
        }
    }

alert(person1.sayNmae==person2.sayNmae)// false

当然这个问题也是能解决的,就是把方法移到外面,让他成为全局的函数,但是这样一来该函数就只能由widows对象调用了,而且如果存在多个方法,就会存在多个全局函数,那我们自定义的的引用类型
就丝毫没有封装可言了。
好在这些问题都可言通过原型模式来解决;
说了一大堆废话进入正题:原型,他是一个对象;

原型模式

实例之New 操作符

创建一个实例通常会经历以下4步:

1、创建一个新的对象;

2、将构造函数的作用域赋给新对象(this指针指向这个新的对象);

3、执行构造函数中的代码(还会将该对象的__proto__指向了函数的prototype对象);

4、返回该对象;

需要注意的地方是当调用构造函数创建一个新对象后,该对象内部将包含一个指正(内部属性
_proto_),指向构造函数的原型对象(prototype
对象),该属性在ES6中貌似规范化了;

function Person{ this.name = name; this.age = age; this.sayName = sayName;}function sayName;}

创建对象方式

但是这样,这个函数就变成了全局函数,而且与 Person
构造函数关联性不强,没有封装性可言。

组合使用构造函数与原型模式

基于上面数据共享的问题,解决方案通常是:属性写在构造函数中,方法写在原型中,这样每个实例都会有自己属性,同时又共享着方法的引用,这样既能最大限度的节省了内存;

 

第二种方法覆盖了整个 prototype 对象,所以需要手动指定 constructor
属性,指向构造函数否则会指向 Object。

总结

1、创建function 就会同时创建它的prototype对象(原型对象),function也是对象其实在原型对象中也存在一个默认的_proto_
指向函数的原型对象(Function.prototype);

2、JS 每个对象中都存在_proto_,函数对象才存在prototype
属性,所以会看到一些博客说JS
对象分为两种:一种是Object(自定义),一种是function;

3、实例话对象就会把function中的prototype属性值赋给对象实例的_proto_;

4、原型是一个对象,我们可以重写该对象;

本人JS新手有误之处还请谅解,顺便指出错误,让我们这些新手有进步!

 

function Person(){}Person.prototype = { constructor : Person, friends : ["greg","jack"]};var person1 = new Person();var person2 = new Person();person1.friends.push;console.log;

原型动态性

原型是对象,对象是引用类型,所以当在原型中干了些什么事情,都能立即在实例对象上反应出来;

也就是说,由构造函数生成的每个对象实例,属性和方法都是独有的,都是复制了一遍。属性独有是必须的,因为这正是对象之间不同的地方,但是很多方法功能和代码都是一样的,重复复制多次,显然就会浪费资源。

2、继承;

Person.prototype.isPrototypeOf;

查找原理

当代码读取某个对象的属性或者方法时都会执行一次搜索,首先从从对象本身开始,如果存在则返回给定的属性名或者方法名,不存在继续搜索指针(_proto_)指向的原型对象,如上面的代码 persong1.sayNmae。首先解析器会问:”对象实例person1有sayName吗?”,答:没有,然后继续搜索,问person1的原型有”sayName
吗?”,答:有,读取原型对象中的函数;这就是多个实例共享原型中属性、方法的基本原理;

由此可知当实例中与原型中存在相同的属性名时会读取实例的属性名,但是他们之间不存在引用问题,即你设置了实例的属性为null,并不会影响到原型中的属性,他们终究是属于不同的对象;

当代码读取某个对象的某个属性,会执行搜索。先从当前对象开始,如果没有,则搜索指针指向的原型对象,而不会搜索构造函数。对象实例可以访问但是不能重写原型对象的值。如果实例中设置了与原型对象同名的属性,则搜索过程,在实例中结束而不会访问原型对象,所以达到覆盖的目的。因此即使这个属性设置为
null,也表示在实例中已经存在该属性,而不会取消掉这个属性,从而可以访问原型对应属性。

function特征

在js中一切都是对象,当我们写function 时js
就会同时创建它的prototype对象(原型对象),这个对象包含一个属性prototype(原型),这个属性是一个指针,该指针指向原型对象,

该对象的作用是特定类型(该类型实例的对象)的所有实例共享属性和方法;

function Person(name,age,job){

    this.name=name;
    this.age=age;
    this.job=job;
}
Person.prototype.sayNmae=function(){
    alert(this.name);
}
var persong1=new Person("Join",18,"农民");
var persong2=new Person("Join",18,"地主");

alert(persong1.sayNmae==persong2.sayNmae)// true

 

person1 = new Person();person2 = new Person();

3.构造函数模式与工厂模式区别:

创建对象的模式

所以用构造函数模式,有类型的区分,可以将它的实例标识为一种特定的类型。

这就是最常用的模式,构造函数用来定义实例属性,通过传递参数实现自定义;原型用来定义方法或者需要所有实例共享的属性。这样,既实现了自定义,又保证了共用,还避免了问题。

此外,构造函数就是普通的函数,因为要反馈得到新对象,所以用 new
来调用。如果不用的话,直接执行就跟普通函数一样,例如上面,执行
Person.sayName() 会弹出 window.name,因为函数在 window 下面执行,所以
this 指向 window。

person1 和 person2 都是通过 Person 这个 Function
类型实例,再次生成的实例,它们俩都有共同的属性 country 和方法
sayName,因为它们都有某个指针,直接指向 Person.prototype
所指向的对象。不过要注意 __proto__ 这个指针是不标准的,只有 Chrome
和 Firefox 等浏览器自己定义的,实际中,也不会用到这个属性,只是作为理解
prototype 来用:

Author

发表评论

电子邮件地址不会被公开。 必填项已用*标注