明白JavaScript 的原型属性

理解JavaScript的原型属性

2016/06/21 · JavaScript
· 2 评论 ·
原型

本文由 伯乐在线 –
alvendarthy
翻译,sunshinebuel
校稿。未经许可,禁止转载!
英文出处:bytearcher。欢迎加入翻译组。

理解 JavaScript
prototype属性不太容易。你也许知道它同面向对象编程(OOP)和对象继承有关,但未必对其技术原理非常清楚。

1.原型继承

  面向对象编程可以通过很多途径实现。其他的语言,比如
Java,使用基于类的模型实现: 类及对象实例区别对待。但在 JavaScript
中没有类的概念,取而代之的是一切皆对象。JavaScript
中的继承通过原型继承实现:一个对象直接从另一对象继承。对象中包含其继承体系中祖先的引用——对象的
prototype 属性。

首先是在访问上的区别,当访问实例对象的某个属性但它本身没有时,它就会到原型中去查找,但不会去查找静态属性。

JavaScript中是没有类的概念的,有的只是原型和对象。

原型的含义是指:如果构造器有个原型对象A,则由该构造器创建的实例(Object
Instance)都必然复制于A。““在JavaScript中,对象实例(Object
Instance)并没有原型,而构造器(Constructor)有原型,属性'<构造器>.prototype’指向原型。对象只有“构造自某个原型”的问题,并不存在“持有(或拥有)某个原型”的问题。

原型继承

面向对象编程可以通过很多途径实现。其他的语言,比如
Java,使用基于类的模型实现: 类及对象实例区别对待。但在 JavaScript
中没有类的概念,取而代之的是一切皆对象。JavaScript
中的继承通过原型继承实现:一个对象直接从另一对象继承。对象中包含其继承体系中祖先的引用——对象的 prototype 属性。

class 关键字是在 ES6 中首次引入 JavaScript
的。其实,它并没有为面向对象继承引入新模型, class
关键字通过语法糖,实现了本文介绍的原型特性和构造函数。

2. JavaScript 实现继承的语言特性

  • 当尝试访问 JavaScript
    对象中不存在的属性时,解析器会查找匹配的对象原型。例如调用
    car.toString(),如果 car 没有 toString 方法,就会调用 car
    对象的原型。 这个查找过程会一直递归,
    直到查找到匹配的原型或者继承链尽头。

  • 调用  new Car()
    会创建一个新的对象,并初始化为 Car.prototype。
    这样就允许为新对象设置原型链。需要注意的是,new Car() 只有当  Car 是函数时才有意义。
    此类函数即所谓构造函数

  • 调用对象的一个成员函数时, this
    的值被绑定为当前对象。例如调用 “abc”.toString(),this 的值被设置为
    “abc”,然后调用 toString 函数。该技术支持代码重用:同样的代码,可在
    this
    为各种不同的值时调用。对象的成员函数,也被称为对象的方法。

   图片 1

  首先,我们定义构造函数 Rectangle。
按照规范,我们大写构造函数名首字母,表明它可以用 new
调用,以示与其他常规函数的区别。构造函数自动将 this
赋值为一空对象,然后代码中用 x 和 y 属性填充它,以备后用。然后,
Rectangle.prototype 新增一个通过 x 和 y 属性计算周长成员函数。 注意 this
的使用,在不同的对象中,this
会有不同的值,这些代码都可以正常工作。最后, 一个名为 rect
的对象创建出来了。 它继承了 Rectangle.prototype, 我们可以调用
rect.perimeter(), 然后将结果打印到控制台。

// 实例对象不会去查找静态属性
function Foo(){}
Foo.a = 1;
var foo = new Foo();
foo.a // undefined

// 当实例对象没有某个属性时,会尝试去原型中查找
function Foo(){}
Foo.prototype.a = 1;
var foo = new Foo();
foo.a // 1

在说原型之前先说说JavaScript中对象的三种类型:

 

JavaScript 实现继承的语言特性

以下语言特性共同实现了 JavaScript 继承。

  • 当尝试访问 JavaScript
    对象中不存在的属性时,解析器会查找匹配的对象原型。例如调用 car.toString(),如果
    car 没有 toString 方法,就会调用 car 对象的原型。
    这个查找过程会一直递归, 直到查找到匹配的原型或者继承链尽头。
  • 调用  new Car() 会创建一个新的对象,并初始化为 Car.prototype
    这样就允许为新对象设置原型链。需要注意的是,new Car()
    只有当  Car 是函数时才有意义。 此类函数即所谓构造函数。
  • 调用对象的一个成员函数时, this
    的值被绑定为当前对象。例如调用 "abc".toString()this
    的值被设置为 "abc",然后调用 toString
    函数。该技术支持代码重用:同样的代码,可在 this
    为各种不同的值时调用。对象的成员函数,也被称为对象的方法。

prototype 属性名称带来的误解

  有一些关于 JavaScript 的原型的误解。
一个对象的原型与对象的 prototype 属性并非一回事。
前者用于在原型链中匹配不存在的属性。后者用于通过 new
关键字创建对象,它将作为新创建对象的原型。
理解二者的差异,将帮助你彻底理解 JavaScript 中的原型特性。

  Rectangle.prototype 是用 new
Rectangle() 创建出来对象的原型, 而 Rectangle 的原型实际上是 JavaScript
的 Function.prototype。(子对象的原型是父对象的 prototype 属性
对象中保存原型的变量,也被称之为内部原型引用(the internal prototype
link),历史上也曾称之为 __proto__ ,对这个称谓始终存在一些争议。
更精确的,它可以被称为 Object.getPrototypeOf(…) 的返回值。

静态方法中的this指向调用它的对象,比如在下面代码中指向的就是调用它的Foo,原型方法中的this指向实例对象

(1)用户创建的对象,也就是用new关键字创建出来的对象。如:

一个自定义的函数,假设为

举个栗子

我们用面向对象编程,实现一个计算矩形周长的例子。

JavaScript

function Rectangle(x, y) { this.x = x; this.y = y; }
Rectangle.prototype.perimeter = function() { return 2 * (this.x +
this.y); } var rect = new Rectangle(1, 2);
console.log(rect.perimeter()); // outputs ‘6’

1
2
3
4
5
6
7
8
9
10
11
function Rectangle(x, y) {
    this.x = x;
    this.y = y;
}
 
Rectangle.prototype.perimeter = function() {
    return 2 * (this.x + this.y);
}
 
var rect = new Rectangle(1, 2);
console.log(rect.perimeter()); // outputs ‘6’

首先,我们定义构造函数 Rectangle
按照规范,我们大写构造函数名首字母,表明它可以用 new
调用,以示与其他常规函数的区别。构造函数自动将 this
赋值为一空对象,然后代码中用 xy 属性填充它,以备后用。

然后, Rectangle.prototype 新增一个通过 xy
属性计算周长成员函数。 注意 this 的使用,在不同的对象中,this
会有不同的值,这些代码都可以正常工作。

最后, 一个名为 rect 的对象创建出来了。
它继承了 Rectangle.prototype, 我们可以调用 rect.perimeter()
然后将结果打印到控制台。

function Foo(){}
Foo.fn = function(){
    console.log(this);
};
Foo.prototype.fn = function(){
    console.log(this);
};
var foo = new Foo();
foo.fn(); // Foo {}
Foo.fn(); // function Foo(){}

         var obj = new Object();

function Person(){
}

prototype 属性名称带来的误解

有一些关于 JavaScript 的原型的误解。
一个对象的原型与对象的 prototype 属性并非一回事。
前者用于在原型链中匹配不存在的属性。后者用于通过 new
关键字创建对象,它将作为新创建对象的原型。
理解二者的差异,将帮助你彻底理解 JavaScript 中的原型特性。

在我们的例子中, Rectangle.prototype 是用 new Rectangle()
创建出来对象的原型, 而 Rectangle 的原型实际上是 JavaScript
的 Function.prototype。(子对象的原型是父对象的 prototype 属性)

对象中保存原型的变量,也被称之为内部原型引用(the internal prototype
link
),历史上也曾称之为 __proto__ ,对这个称谓始终存在一些争议。
更精确的,它可以被称为 Object.getPrototypeOf(...) 的返回值。

2 赞 5 收藏 2
评论

静态属性和原型属性的区别就在于this的指向以及查找规则上,但this的指向问题并不是最重要的,拿实现链式调用来说

(2)构造函数(其实在JavaScript中任何非匿名函数都可以是构造函数)对象。如上面的Object就是个构造函数对象。

当使用 new
创建对象时,创建的对象实际上是对Person原型的一个引用。如下列代码:

关于作者:alvendarthy

图片 2

一个热爱生活的家伙!
个人主页 ·
我的文章 ·
16

图片 3

// 通过原型实现链式调用
function Foo(){}
Foo.prototype.a = function(){
    console.log('a');
    return this;
};
Foo.prototype.b = function(){
    console.log('b');
    return this;
};
var foo = new Foo();
foo.a().b(); // a b

// 通过静态方法实现链式调用
function Foo(){}
Foo.a = function(){
    console.log('a');
    return this;
};
Foo.b = function(){
    console.log('b');
    return this;
};
Foo.a().b(); // a b

(3)原型对象。构造函数的prototype属性所指向的对象。

<script type="text/javascript" >

       function Person(){

       }

       function Person2(){
       }
        var a= new Person();
       Person2.prototype=new Person;

       var b=new Person2();
       var c=new Person2();

       alert(b instanceof Person);

</script>

最大的区别还是在查找规则上,在原型上添加属性可以当做默认属性来使用

上面三类对象的__proto__属性所指向的对象即为该对象的原型,也对应该对象的构造函数的prototype属性。Function.prototype是所有函数的原型,Object.prototype是所有对象的祖先。因为Object是一个构造函数,所有Object.__proto__

Function.prototype。又因为Function.prototype是一个对象(原型对象),所以Function.prototype.__proto__
= Object.prototype。而Function.__proto__ =
Function.prototype。因为Object.prototype是所有对象的祖先所以有Object.prototype.__proto__
= null。另外原型对象中还有一个指向构造函数的属性constuctor。拿Function
和 Object这两个构造函数为例,它们的原型属性Function.prototype 和
Object.prototype
中都有一个constructor属性。其中Function.prototype.constructor =
Function, Object.prototype.constructor = Object。

下面用一个例子来说明:

 function func(){ };

 var f = new func();

console.log( f.__proto__ == func.prototype); // true

console.log( f.__proto__.__proto__ == Object.prototype); //true

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

console.log( func.prototype.__proto__ == Object.prototype); //true

console.log( func.prototype.constructor == func); //true

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

console.log( Object.property.__proto__ == null); //true

   
 在此代码中,我们设定了Person2的原型为Person的一个对象,然后创建了Person2的对象b
,然后 输出b instanceof Persontrue。(instanceof
是js的语法,用于比较对象是否为某个类型
)

function Foo(){}
Foo.prototype.a = 1;
var foo = new Foo(),
    foo2 = new Foo();
foo2.a = 2;
foo.a // 1
foo2.a // 2

 

每个实例对象都可以拥有自己的属性和方法,在没有设置的情况下才会去尝试使用prototype上的属性和方法,而静态方法是无法实现这种效果的。

 

    此为截图:

       图片 4

 

 

       这里有一篇较为详细的介绍:

发表评论

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