构造函数,原型,实例的关系

构造函数

特点:

    a:构造函数的首字母必须大写,用来区分于普通函数

    b:内部使用的this对象,来指向即将要生成的实例对象

    c:使用New来生成实例对象

function Person(name, age, job){
  this.name = name;
  this.age = age;
  this.job = job;
  this.friends = ["Shelby", "Court"];
}

实例

实例通过构造函数使用new创建的,在此过程中,经历了:
1,创建一个新对象,并将this指向新对象
2,执行构造函数(要是给this添加了属性和方法,新对象将拥有了这些属性和方法)
4,返回这个新对象,即给出实例。
function Person(name,age){
    this.name = name;
    this.age = age;
    this.sayHello = function(){
        console.log(this.name + "say hello");
    }
}
var girl = new Person("bella",23);
var boy = new Person("alex",23);
console.log(girl.name);  //bella
console.log(boy.name);   //alex
console.log(girl.sayHello === boy.sayHello);  //false

构造函数在不返回值的情况下默认返回新对象

原型

每个函数都有prototype属性,这个属性指向一个对象,叫原型对象。

由同一个构造函数的实例都继承了这个构造函数原型对象上的属性和方法。

prototype 属性是不可枚举的,因此使用 for-in 无法发现

 

原型的运用

上面例子中:一个构造函数Person生成了两个对象实例girl和boy,并且有两个属性和一个方法,当New一个实例对象的时候,都会去创建一个sayHello方法,这就浪费了内存资源,因为sayHello方法使一样的行为的,完全可以被两个实例对象共享。

通过原型对象,所有实例都可以访问到的一个公共容器,那么我们就将重复的东西放到公共容器就好了。

原型都有constructor属性,指向构造函数,既然是原型的属性,而实例又继承了原型的属性和方法,所以它被实例对象继承。
ps:由于constructor属性是一种原型对象和构造函数的关系,所以在修改原型对象的时候,一定 要注意construtor的指向问题,避免instanceof失真,关于这一点,会在继承中讲到。
//变量 oStringObject 是否为 String 对象的实例
var
oStringObject = new String("hello world"); console.log(oStringObject instanceof String); // 输出 "true"

原型链

对象都有一个__propo__内置属性,用于指向创建它的函数对象的原型对象(所以实例有个属性__proto__指向构造函数的原型对象)

举个栗子说明原型链
   console.log(girl.__proto__ === Person.protype);//true
  console.log(Persion.propotype.__proto__ === Object.propotype);//true
  console.log(Object.porpotype.__proto__); //null
  

通过__proto__串起来直到Object.propotype.__proto__为null的链叫做原型链。

 
实例不能重写原型的值。只能通过采取同名属性覆盖,可以通过delete删除实例的这个属性;因为搜索实例的属性时,是先搜索实例,实例没有这个属性的话会在原型里去搜索
 

原型的重写

Person.prototype={这个原型所有的属性和方法写在这里面}。//直接给原型赋值了一个对象
这样就是重写原型,因为原型对象是个对象,只要是以对象形式赋值给它就会被重写,原型指向的对象就发生了变化。
要是里面没有constructor属性,那么原型就不再拥有这个属性,而变成了这个新对象的属性,constructor指向了Object。
 
注意:
1,重写原型的constructor属性会让这个属性的枚举属性变成可枚举,但原生的是不可枚举的。可以通过Object.defineProperty来重写constructor属性,这样重写成不可枚举
Object.defineProperty(person.prototype,"constructor",{
enumerable:false,
value:person
})
2,调用构造函数时会为实例添加一个指向最初原型的[[Prototype]]指针,而把原型修改为另外一个对象(重写原型)就等于切断了构造函数与最初原型之间的联系。
需要手动将constructor指回去。不然之前的实例无法继承新的原型。之后new的实例能继承新原型,即使constructor没有指回去
请记住:实例中的指针仅指向原型,而不指向构造函数。
 
3、原型的属性里面有引用类型的话,在实例中调用这个引用类型的方法去修改属性值会导致所有实例里这个值被改变。若要单独改变,则要用覆盖的方法,即给这个属性重新赋值,这样就仅仅是覆盖原型里的同名属性
 
如:原型有个属性:label:["student","child"](对于这种属性值是引用类型的处理是把这个写在构造函数里this.,实例就可以单独处理自己的这个属性)
 
person1.label.push("at1");会更改原型的属性值导致所有实例的这个值被修改
person1.label=["at1"];这样去覆盖就不会修改原型对应属性的值
 
4、可以给原生对象的引用类型定义新的方法供使用。例:Array.prototype.newskill
5,原型与构造函数组合使用,不会涉及到引用的问题。因为每次new,构造函数中有引用的话,每new一次就创建一个不同的对象。原型中,因为实例是不能修改原型的,所以也不会涉及到因为引用影响到其它的实例继承。
 

实例的基本属性和方法

1,constructor,指向实例的构造函数。注意:原型的constructor也指向构造函数,如:
Person.prototype. constructor 指向 Person
 
2,hasOwnProperty(propertyName):自有属性,不在原型中
3,isPrototypeOf(object):当前对象是否是传入对象的原型
4,propertyIsEnumerable(propertyName),检查属性是否可枚举(能用for-in遍历)
返回对象的字符串形式:
5,toLocaleString():该字符串与执行环境的地区对应
6,toString()
7,valueOf():返回对象的字符串、数值或布尔值表示,通常与 toString()方法的返回值相同。
 

 
 

posted @ 2019-02-16 16:29  中二的羊  阅读(1510)  评论(0编辑  收藏  举报