When I first started learning about JavaScript object model my reaction was of horror and disbelief. I was totally puzzled by its prototype nature as it was my first encounter with a prototype based language. I didn’t help that JavaScript has a unique take on prototypes as it adds the concept of function constructors. I bet that many of you have had similar experience.

But as I used JavaScript more I didn’t just learn to understand its object model but also started love parts of it. Thanks to JavaScript I have find out the elegance and flexibility of prototypes languages. I am now quite fond of prototype languages because they have a simpler and more flexible object model than class based languages.

Prototypes in Javascript

Most guides / tutorials start explaining JavaScript objects by going directly to ‘constructor functions’, I think this is a mistake, as they introduce a fairly complex concept early on making Javascript look difficult and confusing from the start. Let’s leave this for later. First let’s start with the basics of prototypes.

Prototype chains (aka prototype inheritance)

Every object in Javascript has a prototype. When a messages reaches an object, JavaScript will attempt to find a property in that object first, if it cannot find it then the message will be sent to the object’s prototype and so on. This works just like single parent inheritance in a class based language.

Prototype inheritance chains can go as long as you want. But in general it is not a good idea to make long chains as your code can get difficult to understand and maintain.

The __proto__ object

To understand prototype chains in JavaScript there is nothing as simple as the __proto__property. Unfortunately __proto__ is not part of the standard interface of JavaScript, not at least until ES6. So you shouldn’t use it in production code. But anyway it makes explaining prototypes easy.

// let's create an alien object
var alien = {
kind: 'alien'
} // and a person object
var person = {
kind: 'person'
} // and an object called 'zack'
var zack = {}; // assign alien as the prototype of zack
zack.__proto__ = alien // zack is now linked to alien
// it 'inherits' the properties of alien
console.log(zack.kind); //=> ‘alien’ // assign person as the prototype of zack
zack.__proto__ = person // and now zack is linked to person
console.log(zack.kind); //=> ‘person’

As you can see the __proto__ property is very straightforward to understand and use. Even if we shouldn’t use __proto__ in production code, I think that these examples give the best foundation to understand the JavaScript object model.

You can check that one object is the prototype of another by doing:

console.log(alien.isPrototypeOf(zack))
//=> true

Prototype lookups are dynamic

You can add properties to the prototype of an object at any time, the prototype chain lookup will find the new property as expected.

var person = {}

var zack = {}
zack.__proto__ = person // zack doesn't respond to kind at this point
console.log(zack.kind); //=> undefined // let's add kind to person
person.kind = 'person' // now zack responds to kind
// because it finds 'kind' in person
console.log(zack.kind); //=> 'person'

New / updated properties are assigned to the object, not to the prototype

What happens if you update a property that already exists in the prototype? Let’s see:

var person = {
kind: 'person'
} var zack = {}
zack.__proto__ = person zack.kind = 'zack' console.log(zack.kind); //=> 'zack'
// zack now has a 'kind' property console.log(person.kind); //=> 'person'
// person has not being modified

Note that the property ‘kind’ now exists in both person and zack.

Object.create

As explained before __proto__ is not a well supported way of assigning prototypes to objects. So the next simplest way is using Object.create(). This is available in ES5, but old browsers / engines can be shimmed using this es5-shim.

var person = {
kind: 'person'
} // creates a new object which prototype is person
var zack = Object.create(person); console.log(zack.kind); // => ‘person’

You can pass an object to Object.create to add specific properties for the new object

var zack = Object.create(person, {age: {value:  13} });
console.log(zack.age); // => ‘13’

Yes, the object you need to pass is a bit convoluted, but that is the way it is. See the docs here.

Object.getPrototype

You can get the prototype of an object using Object.getPrototypeOf

var zack = Object.create(person);
Object.getPrototypeOf(zack); //=> person

There is no such thing as Object.setPrototype.

Constructor Functions

Constructor functions are the most used way in JavaScript to construct prototype chains. The popularity of constructor functions comes from the fact that this was the only original way for constructing types. It is also an important consideration the fact that many engines are highly optimized for constructor functions.

Unfortunately they can get confusing, they are in my opinion one of the main reasons why new comers find JavaScript puzzling, but they are a big part of the language and we need to understand them well.

Functions as constructors

In JavaScript you create an instance of a function like this:

function Foo(){}

var foo = new Foo();

//foo is now an instance of Foo
console.log(foo instanceof Foo ) //=> true

In essence functions when used with the keyword new behave like factories, meaning that they create new objects. The new object they create is linked to the function by its prototype, more on this later. So in JavaScript we call this an instance of the function.

‘this’ is assigned implicitly

When we use ‘new’, JavaScript injects an implicit reference to the new object being created in the form of the ‘this’ keyword. It also returns this reference implicitly at the end of the function.

When we do this:

function Foo() {
this.kind = ‘foo’
} var foo = new Foo();
foo.kind //=> ‘foo’

Behind the scenes it is like doing something like this:

function Foo() {
var this = {}; // this is not valid, just for illustration
this.__proto__ = Foo.prototype; this.kind = ‘foo’ return this;
}

But keep in mind that the implicit ‘this’ is only assigned to a new object when using ‘new’. If you forget ‘new’ keyword then ‘this’ will be the global object. Of course forgetting new is a cause of multiple bugs, so don’t forget new.

One convention that I like is capitalizing the first letter of a function when it is intented to be used as a function constructor, so you now straightaway to you are missing the new keyword.

The ‘function prototype’

Every function in JavaScript has a special property called ‘prototype’.

function Foo(){
} Foo.prototype

As confusing as it may sound, this ‘prototype’ property is not the real prototype (__proto__) of the function.

Foo.__proto__ === Foo.prototype //=> false

This of course generates a lot of confusion as people use the term ‘prototype’ to refer to different things. I think that a good clarification is to always refer to the special ‘prototype’ property of functions as ‘the function prototype’, never just ‘prototype’.

The ‘prototype’ property points to the object that will be asigned as the prototype of instances created with that function when using ‘new’. Confusing? This is easier to explain with an example:

function Person(name) {
this.name = name;
} // the function person has a prototype property
// we can add properties to this function prototype
Person.prototype.kind = ‘person’ // when we create a new object using new
var zack = new Person(‘Zack’); // the prototype of the new object points to person.prototype
zack.__proto__ == Person.prototype //=> true // in the new object we have access to properties defined in Person.prototype
zack.kind //=> person

That is mostly everything there is to know about the JavaScript object model. Understanding how __proto__ and function.prototype are related will give you countless hours of joy and satisfaction, or maybe not.

Mistakes, confusing? Let me know.

A Plain English Guide to JavaScript Prototypes的更多相关文章

  1. [转]A plain english introduction to cap theorem

    Kaushik Sathupadi Programmer. Creator. Co-Founder. Dad. See all my projects and blogs → A plain engl ...

  2. [label][JavaScript][The Defined Guide of JavaScript] 如何声明变量

    因为觉得我自己的JavaScript基础很不扎实,或者可以说根本就没有所谓基础,所以就最近一直在看<The Defined Guide of JavaScript> . 在一边看的同时,我 ...

  3. [label][JavaScript][The Defined Guide of JavaScript] 变量的作用域

    变量的作用域 一个变量的作用域(scope)是程序中定义这个变量的区域. 全局(global)变量的作用域(scope)是全局性的,即在JavaScript代码中,它处处都有定义.    而在函数之内 ...

  4. 《JS高程》对象&原型学习笔记

    ECMA-262 把对象定义为:”无序属性的集合,其属性可以包含基本值.对象或者函数.”可以把 ECMAScript 的对象想象成散列表:无非就是一组名值对,其中值可以是数据或函数.   6.1.理解 ...

  5. Cheatsheet: 2019 07.01 ~ 09.30

    Other Intro Guide to Dockerfile Best Practices QuickJS Javascript Engine Questions for a new technol ...

  6. 每个JavaScript开发人员应该知道的33个概念

    每个JavaScript开发人员应该知道的33个概念 介绍 创建此存储库的目的是帮助开发人员在JavaScript中掌握他们的概念.这不是一项要求,而是未来研究的指南.它基于Stephen Curti ...

  7. Tomcat Clustering - A Step By Step Guide --转载

    Tomcat Clustering - A Step By Step Guide Apache Tomcat is a great performer on its own, but if you'r ...

  8. Javascript modules--js 模块化

    https://medium.freecodecamp.org/javascript-modules-a-beginner-s-guide-783f7d7a5fcc 这个网站也是非常好:https:/ ...

  9. JavaScript一词被《牛津大词典》收录了

    早上看VS Team的推特发了这个图片,以前总爱问Java怎么读,现在好了,有标准发音了. 确定是 扎瓦·死磕瑞普特 ,哈哈,以后不要再念加瓦了. …… Last month JavaScript r ...

随机推荐

  1. C#<热血传奇>服务端源代码再次给力更新

    前段时间一直在忙公司项目,最近抽点空稍微把部分代码重新整理一下(代码太久没碰很多地方都忘记了 囧~~~) 此次还是更新服务端,并修正上一版里面存在的很多指针 异常BUG...  (代码比较烂,还请各位 ...

  2. asp.net中对象的序列化,方便网络传输

    对象序列化 是将对象状态转换为可保持或传输的格式的过程.反序列化 是将流转换为对象序列化和反序列化相结合 可以使对象数据轻松的存储和传递 在 .NET 中,如果是对象可序列化,需要在 声明对象的开始部 ...

  3. hdu 3717

    思路:二分答案,然后模拟消灭石头的过程: 如果单纯的暴力模拟的话,肯定会T的: 所以要用到一定的技巧来维护: 在网上看到大神们用O(n)的复杂度来优化,真心orz: 原理是这样的:用一个变量sum_2 ...

  4. 《php和mysql web开发》读书笔记

    总算是强迫自己把第一篇给看完了,在这里做一个小结,将一些知识点记录下来. 一.第一篇 使用PHP 1.php中的注释.php支持c.c++和shell脚本风格注释 /**/多行注释  //单行注释   ...

  5. 解决Ubuntu root账户的问题

    问题的提出:在Linux环境下,许多操作需要有管理员权限才能进行.如果没有root权限,就连基本的文件拷贝操作都只能在用户文件夹下进行,而对于Ubuntu系统,安装时是没有设定root帐号的,那么怎样 ...

  6. 李洪强实现横向滚动的View<二>

    上一节中我们已经实现了一个带有图片,标题和价格label 的UIView 这节我们用这个view实现一个横向滚动的UIcollectionView 实现的效果如下:  01 - 创建CFTyreScr ...

  7. Delphi编写自定义控件以及接口的使用(做了一个TpgDbEdit)

    写给觉得自己编写Delphi很复杂的人,包括自己. Delphi自己写控件其实并不难,难的在于开发复杂的控件.(其实,编程,很多东西都是会了就不难,因此,我怕自己日后觉得自己写控件很难,特意在这记录自 ...

  8. 转:HTTP请求(GET、POST和soap区别)和响应

    一直对Http请求和SOAP请求不是太理解,只是知道SOAP是基于Http的,并且增加了很多XML标签,SOAP经常用在WebService中,比如在C#中创建一个WebService,然后在客户端生 ...

  9. free 命令解释

    free 命令 buffers and cached 解释 N多人总是询问,当在linux在输入free时内存总数怎么加起来不一样啊,下面我来解释一下free命令的输出. 我们运行free命令时都会看 ...

  10. char*,const char*和string的相互转换

    好久没写东西啦,发表学术文章一篇,hiahia~ 近日和小佳子编程时遇到很多转换问题,很麻烦,在网上查了很多资料. 为了以后查找方便,特此总结如下. 如果有不对的地方或者有更简单的方法,请指出~~ 1 ...