摘要:深入理解原型与继承看过不少书籍,不少文章,对于原型与继承的说明基本上让人不明觉厉,特别是对于习惯了面向对象编程的人来说更难理解,这里我就给大家说说我的理解。
深入理解:JavaScript原型与继承
看过不少书籍,不少文章,对于原型与继承的说明基本上让人不明觉厉,特别是对于习惯了面向对象编程的人来说更难理解,这里我就给大家说说我的理解。
首先JavaScript是一门基于原型编程的语言,它遵守原型编程的基本原则:
所有的数据都是对象(javascript中除了字符串字面量、数字字面量、true、false、null、undefined之外,其他值都是对象!)
要得到一个对象,不是通过实例化类,而是找到一个对象作为原型并克隆它(new操作符)
对象会记住它的原型(JavaScript中通过隐藏的__proto__属性)
如果对象无法响应某个请求,它会把该请求委托给它自己的原型
这么说来,JavaScript一定有一个根对象,所有的对象的最终原型都将是它,它就是Object.prototype(有的人说跟对象是null,因为Object.prototype.__proto__为null),Object.prototype也是一个对象,它是一个空的对象。(记住一点:所有的原型都是对象,但不是函数,虽然函数也是对象,Object其实就是一个函数,而Object.prototype是一个对象)
以下代码创建空对象:
var obj1 = new Object(); var obj2 = {}; Object.getPrototypeOf(obj1) === Object.prototype; //true Object.getPrototypeOf(obj2) === Object.prototype; //true
我们再来看下以下代码:
function Book(name){ this.name = name; } Book.prototype.getName = function(){ return this.name; } Book.num = 5; var book1 = new Book("javascript"); book1.getName(); //javascript Object.getPrototypeOf(book1) === Book.prototype; //true
我们通常说,使用了new Book()来创建了Book的实例book1,但是JavaScript并没有类的概念,这里的Book本质上只是一个函数而已,如果用new则把它当着构造函数对待,那么var book1 = new Book("javascript)是怎么个执行过程呢?在这之前,我们来先看下Function与Object的关系
这里有张图:来源于javascriptcn
console.log(Function); //Function 函数 console.log(Function.constructor); //Function 函数 console.log(Function.__proto__); //function(){} 空函数 console.log(Function.__proto__.__proto__);//{} 空对象,即Object.prototype console.log(Function.prototype); //function(){} 空函数 console.log(Function.constructor.prototype); //function(){} 空函数 console.log(Object); //function Object(){[native code]} Object是个函数 console.log(Object.__proto__); //function(){} 空函数 console.log(Object.prototype); //{} 空对象 console.log(Object.constructor); //Function 函数 console.log(Object.constructor.prototype); //function(){} 空函数
以上测试说明什么?
说明了内置函数Function以它自身为构造函数,且以它自身为原型,这个原型是个空函数;
Object是个函数,Object是以内置函数Function为构造函数的,它自己的原型Object.prototype是个空对象
我总结了这么个东西:在JavaScript中,最原始的东西有两个,就是function(){}空函数 和 {}空对象,但它们都有内置的属性、方法,所有的JS对象都是基于它们创建出来的;
Function和Object就像是女娲和伏羲,Object提供种子(Object.prototype),Function负责繁衍(Function.prototype)。Object的实例是由Object.prototype提供的种子经过Function.prototype(function(){} 空函数)打造出来的。Object.prototype这个基因会被一直继承下去,并一代一代增强。
每一个函数都有一个原型对象(prototype)和隐藏的__proto__属性,函数的__proto__属性指向Function.prototype,而原型对象(prototype)是一个对象,符合以下第2点(也有构造函数constructor和隐藏的__proto__属性);
每一个对象都有一个构造函数(constructor)和隐藏的__proto__属性,constructor自然指的是它的构造函数,而__proto__指向的是它的构造函数的原型对象prototype,而对象的原型prototype对象同时又包含了该对象构造函数、还具有自己的__proto__属性;
通过__proto__属性,每个对象和函数都会记住它的原型,这样就形成了原型链;
console.log(Book); //Book函数自身 console.log(Book.__proto__); //function(){} 空函数 console.log(Book.prototype); //Book的原型对象,包含了构造函数constructor、__proto__ console.log(Book.prototype.__proto__); //{} 等于Object.prototype console.log(Book.prototype.constructor); //Book函数自身 console.log(Book.constructor); //Function函数 console.log(Book.constructor.prototype); //function(){} 空函数(Function函数的原型) console.log(Book.__proto__.__proto__); //{} 等于Object.prototype
每一个函数都是通过Function构造出来的,函数的原型属性__proto__指向Function.prototype,而函数的原型对象prototype是代表着自身的对象,它的__proto__属性指向Object.prototype,它的constructor属性默认指向它自己构造函数(也可改为别的函数,如:Book.prototype.constructor = Person;)。
console.log(book1); //Book { name: "javascript" } console.log(book1.__proto__); //指向Book.prototype对象 console.log(book1.prototype); //undefined console.log(book1.constructor); //指向Book函数 console.log(book1.constructor.prototype); //Book.prototype对象
所以,我们通常说‘一个对象的原型’其实是不准确的,应该是‘一个对象的构造器的原型’,且对象是把它无法响应的请求委托给它的构造器的原型顺着原型链往上传递的。
现在来讲解一下var book1 = new Book("javascript)的执行过程,是这样:new先通过Function.prototype从Object.prototype克隆一个空对象,然后将空对象的构造函数constructor指定为Book,并将该空对象的__proto__属性指向它的构造函数的原型Book.prototype,之后通过Book构造函数对这个空对象进行赋值操作,最后将这个对象返回给变量book1。
我们再看如下代码:
var obj = {name: "Sufu"}; var Person = function(){}; Person.prototype = obj; var a = new Person(); console.log(a.name); //Sufu
这段是这样执行的:
首先尝试查找对象a的name属性,找不到,执行第2步
将查找name属性的这个请求委托给a的构造器的原型,a.__proto__记录的是Person.prototype,Person.prototype.__proto__记录的是obj
在对象obj中查找name,找到并返回它的值给a,假如还找不到,它就通过obj.__proto__找到Object.prototype,而Object.prototype.__proto__为null,所以找不到就返回undefined。
总之,对象、函数都有一个隐藏的__proto__属性,这个属性就是原型链上的环,一环扣一环,从而形成了原型链!
好了,就介绍到这里了,以上是个人的理解,有不对的地方欢迎指出!
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/80463.html
摘要:深入之继承的多种方式和优缺点深入系列第十五篇,讲解各种继承方式和优缺点。对于解释型语言例如来说,通过词法分析语法分析语法树,就可以开始解释执行了。 JavaScript深入之继承的多种方式和优缺点 JavaScript深入系列第十五篇,讲解JavaScript各种继承方式和优缺点。 写在前面 本文讲解JavaScript各种继承方式和优缺点。 但是注意: 这篇文章更像是笔记,哎,再让我...
摘要:原型链与继承当谈到继承时,只有一种结构对象。如果对该图不怎么理解,不要着急,继续往下看基于原型链的继承对象是动态的属性包指其自己的属性。当使用操作符来作用这个函数时,它就可以被称为构造方法构造函数。 原型链与继承 当谈到继承时,JavaScript 只有一种结构:对象。每个实例对象(object )都有一个私有属性(称之为proto)指向它的原型对象(prototype)。该原型对象也...
摘要:首先,需要来理清一些基础的计算机编程概念编程哲学与设计模式计算机编程理念源自于对现实抽象的哲学思考,面向对象编程是其一种思维方式,与它并驾齐驱的是另外两种思路过程式和函数式编程。 JavaScript 中的原型机制一直以来都被众多开发者(包括本人)低估甚至忽视了,这是因为绝大多数人没有想要深刻理解这个机制的内涵,以及越来越多的开发者缺乏计算机编程相关的基础知识。对于这样的开发者来说 J...
摘要:原文发自我的博客易企秀招聘啦首先我们先来回顾以下中出现的原型继承原型继承自如果我们要在上查询一个定义在的属性会先在上查找如果没有查到那么会顺着原型链去查找所以以下判别式均为如果我们做如下操作原型链并没有被访问一个新的会被加入到的属性中去新的 原文发自我的博客 xiaoyu2er.github.io 易企秀招聘啦! JavaScript Prototypal Inheritance 首先...
摘要:深入系列的第一篇,从原型与原型链开始讲起,如果你想知道构造函数的实例的原型,原型的原型,原型的原型的原型是什么,就来看看这篇文章吧。让我们用一张图表示构造函数和实例原型之间的关系在这张图中我们用表示实例原型。 JavaScript深入系列的第一篇,从原型与原型链开始讲起,如果你想知道构造函数的实例的原型,原型的原型,原型的原型的原型是什么,就来看看这篇文章吧。 构造函数创建对象 我们先...
阅读 946·2021-11-23 10:11
阅读 3802·2021-11-16 11:50
阅读 857·2021-10-14 09:43
阅读 2662·2021-10-14 09:42
阅读 2672·2021-09-22 16:02
阅读 1028·2019-08-29 10:57
阅读 3342·2019-08-29 10:57
阅读 2220·2019-08-26 13:52