fred-ye / summary

my blog
43 stars 9 forks source link

JavaScript中原型的使用 #60

Open fred-ye opened 6 years ago

fred-ye commented 6 years ago

这篇文章来自于我的博客, 后面会慢慢的把东西整理整理移到博客上去。

每个函数都有一个prototype属性,该属性指向一个对象。而该属性只有在该函数做为构造器时才会发生作用,当函数作为构造器时,通过new去创建对象, 对象的原型(内置的prototype属性)会指向构造器(函数)的prototype属性。对象会自动拥有这个对象的构造函数的prototype的成员属性和方法.

先看一个示例:

function P () {
}

typeof P.prototype;
//"object"
//prototype指向的是一个对象

我们来修改P的prototype属性.

var protoObj = {
    date: new Date(),
    say: function() {
        console.log('Hello World');
    }
}
P.prototype = protoObj;

var obj = new P();
obj.date;
obj.say();

当我们将P作为构造器来创建obj时,obj就会拥有对P.prototype的访问权限。obj便可以将P.prototype对应的对象中的属性当作自己的属性来用。

将方法挂到类的原型上

我们定义一个构造函数,包含属性和方法,代码如下:

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

这种方式,每次生成一个对象时会创建一个函数对象用来获取name属性 再看一种定义类的方式:

function getName() {
    return this.name;
}

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

和上一种写法相比,我们只需要创建一次方法函数,在构造函数中引用它们。更优雅的写法如下,将方法挂到原型上。

function Person(name, age) {
    this.name = name;
    this.age = age;
}
Person.prototype.getName = function() {
    return this.name;
}

Person.prototype可以被Person的所有实例共享。JavaScript 允许在程序中的任何时候修改原型(prototype)中的一些东西,也就是说可以在运行时(runtime)给已存在的对象添加额外的方法。如给String添加一个方法来返回逆序的字符串。

var s = "Simon";
s.reversed(); // TypeError on line 1: s.reversed is not a function

String.prototype.reversed = function() {
    var r = "";
    for (var i = this.length - 1; i >= 0; i--) {
        r += this[i];
    }
    return r;
}
s.reversed(); // nomiS

此部份参看这里

利用原型添加属性

有以下代码:

function Gadget(name, color) {
    this.name = name;
    this.color = color;
}

添加原型的两种方式:

Gadget.prototype = {
    price: 100,
    rating: 3
}

原型的使用

javascript中每个对象中并没有属于自己的原型副本,我们可以随时修改prototype属性,并且由同一构造器创建的所有对象的prototype属性都会同时改变

自身属性和原型属性

var newtoy = new Gadget('test', 'red');

当我们在调用对象的某个属性时,javascript引擎会查询该对象的所有属性,如果没有找到。则会去查询创建这个对象的构造器函数的原型newtoy.constructor.prototype,如果在原型中找到了这个属性,就使用该属性。

function Gadget(name, color) {
    this.name = name;
    this.coloe = color;
    this.getName = function() {
        return this.name;
    }
}
Gadget.prototype.price = 100;
Gadget.prototype.rating = 3;

var newtoy = new Gadget('webcam', 'black');
newtoy.propertyIsEnumerable('name'); //true;
newtoy.propertyIsEnumerable('constructor'); //内建属性和方法通常是不可枚举的
newtoy.propertyIsEnumerable('price'); //false; 原型链中的属性也是不可枚举的

但是,newtoy.constructor.prototype.propertyIsEnumerable('price')返回的是true; 其它很好理解,因为price原本就在newtoy.constructor.prototype这个对象上。

isPrototypeOf()

每一个对象都有一个isPrototypeOf方法,用来判断当前对象是否是另一个对象的原型。

getPrototypeOf()

ES5中提供的获取某个对象的原型的方法。

var mokey = {
    hair: true,
    feeds: 'banana'
}
function Human(name) {
    this.name = name;
}
Human.prototype = mokey;
var george = new Human('George');
Object.getPrototypeOf(george).feeds; // banana

对于另一部份实现了ES5的功能,但没有getPrototypeOf方法的浏览器,我们可以采用__proto__

扩展内建对象

采用原型来扩展内建对象

var colors = ['red', 'green', 'blue']; colors.inArray('red');


* 字符串反转

String.prototype.reverse = function() { return Array.prototype.reverse.call(this.split('')).join(''); }

### `__proto__` 链接 (该属性非标准,先不做深入了解)

`__proto__`和`prototype`不是等价的,`__proto__`是某个实例对象的属性,而`prototype`则是构造器的属性。当一个对象被创建时,它的`__proto__` 属性和内部属性[[Prototype]]指向了相同的对象 (也就是它的构造函数的prototype属性).

// 声明一个函数作为构造函数 function Employee() { / 初始化实例 / }

// 创建一个Employee实例 var fred = new Employee();

// 测试相等性 fred.proto === Employee.prototype; // true


### 原型的陷阱;

当我们对原型对象进行替换时,会触发原型链上的异常

function Dog() { this.tail = true; } var benji = new Dog(); var rusty = new Dog(); Dog.prototype.say = function() { return 'woof'; }

然后我们对`Dog`的原型进行如下替换:

Dog.prototype = { paws: 4, hair: true }

再来看原有对象:

typeof benji.paws; //undefined benji.say();// woof

之前创建的对象使用的是之前的`prototype`,我们来新建对象

var lucy = new Dog(); lucy.paws; //4 lucy.say;//undefined lucy.constructor//function Object() { [native code] } typeof lucy.proto.say; //undefined typeof lucy.proto.paws; //number

会发现,其`constructor`的指向都不对了,为此,我们需要重写`constructor`。

Dog.prototype.constructor = Dog;