思维导图
1. js的发展历史
1.1 javaScript的诞生
JavaScript因为互联网而生,紧随着浏览器的出现而问世。
1994年,网景公司(Netscape)发布了Navigator浏览器0.9版。这是历史上第一个比较成熟的网络浏览器,轰动一时。但是,这个版本的浏览器只能用来浏览,不具备与访问者互动的能力。比如,如果网页上有一栏"用户名"要求填写,浏览器就无法判断访问者是否真的填写了,只有让服务器端判断。如果没有填写,服务器端就返回错误,要求用户重新填写,这太浪费时间和服务器资源了。因此,网景公司急需一种网页脚本语言,使得浏览器可以与网页互动。
网页脚本语言到底是什么语言?网景公司当时有两个选择:一个是采用现有的语言,比如Perl、Python、Tcl、Scheme等等,允许它们直接嵌入网页;另一个是发明一种全新的语言。这两个选择各有利弊。第一个选择,有利于充分利用现有代码和程序员资源,推广起来比较容易;第二个选择,有利于开发出完全适用的语言,实现起来比较容易。
1995年收到Java面向对象语言的影响,网景公司和Sun联盟,开发一种可以运行在网页中的语言,并命名为 "Java+script"
1.2 js的设计思想
1995年5月,Brendan Eich只用了10天,就设计完成了这种语言的第一版。它是一个大杂烩,语法有多个来源:
- 基本语法:借鉴C语言和Java语言。
- 数据结构:借鉴Java语言,包括将值分成原始值和对象两大类。
- 函数的用法:借鉴Scheme语言和Awk语言,将函数当作第一等公民,并引入闭包。
- 原型继承模型:借鉴Self语言(Smalltalk的一种变种)。
- 正则表达式:借鉴Perl语言。
- 字符串和数组处理:借鉴Python语言。
2. js继承的思想设计
JavaScript中的数据类型都是对象,必须有一种机制,将所有的对象联系起来,于是设计了 "继承"。es6之前是没有类的概念的(设计者认为如果引入class类的概念, js就是一种完整的面向对象编程语言,过于正式,而且增加了入门难度)
Java 中生成实例
Person p = new Person()
因此JS 引入了new命令,用来生成实例对象,但是js没有类的概念,类比c++和Java 都会调用类的构造函数(constructor),简化之后在js中 new命令后面跟的不是类 而是 构造函数
JavaScript生成实例
function Person(name){
this.name = name
this.type="人"
}
var p1 = new Person("张三") // 实例
var p2 = new Person("李四") // 实例
new 运算符的缺点
无法共享属性和方法;每一个实例对象,都有自己的属性和方法的副本。这不仅无法做到数据共享,也是极大的资源浪费。
例如:p1修改了type属性之后,p2不会被影响
p1.type ="动物" console.log(p2)//{name:"李四",type:"人"}
3. 为什么要设计原型对象prototype
为了解决共享问题,Brendan Eich 为构造函数设置了一个prototype属性。这个属性指向一个对象(原型对象),所有实例共享的属性和方法,都存放在这个对象里面。不需要共享的属性和方法(私有属性、方法)则放在构造函数中。实例对象一旦创建,将自动引用prototype对象的属性和方法。也就是说,实例对象的属性和方法,分成两种,一种是本地的,另一种是引用的。
Animal.prototype.type="动物"
Animal.prototype.say = function(content){
console.log(`${this.name},${content}`)
}
var dog = new Animal('dog')
dog.say('汪汪汪') // dog,汪汪汪
var cat = new Animal('cat')
cat.say('喵喵喵') // cat,喵喵喵
4.什么是原型对象
概念:每个JavaScript对象(除了null)创建的时候,都会与之关联一个对象,这个对象就是原型对象,每个实例对象都会从原型对象中继承属性。
构造函数和实例原型的关系
每个实例对象都有一个隐式原型__proto__属性,指向他的实例原型
function Animal() {
}
var animal = new Animal();
console.log(animal .__proto__ === Animal.prototype); // true
5. 实例对象、原型对象、构造函数的关系
constructor:每个原型都有一个constructor属性,指向其关联的构造函数
function Animal() {
}
console.log(Animal.prototype.constructor === Animal) // true
你是否真的理解了?看看这张图
补充:在js中,所有的function函数都是由Function继承来的,可以说是Function是所有 function的祖宗。而Function则是由自己衍生来的,如图中它的__proto__指向了自己的原型对象
总结
- 所有实例对象的__proto__属性,都指向其构造函数的原型对象
- 所有的函数(构造函数),都是new Function()的实例,所以函数的__proto__ 指向Function构造函数的原型对象Function.prototype
- 所有的的原型对象,都是new Object()的实例,所以原型对象的__proto__指向Objec构造函数的原型对象Object.prototype, 而Object.prototype 指向null
- Function构造函数本身就是Function的实例,所以_proto_指向Function的原型对象。
6.什么是原型链
顾名思义原型链是一条链,既然每个对象都有一个_proto_属性指向原型对象,那么原型对象也有_proto_指向原型对象的原型对象,直到指向中的null,这才到达原型链的顶端。
7.拓展知识点
获取对象的原型对象
function Animal() {
}
var animal = new Animal()
console.log(animal)
console.log(Object.getPrototypeOf(animal)) // 推荐
console.log(animal.__proto__) // 不推荐
console.log(animal.constructor.prototype) // 不推荐 利用每个实例对象都从原型中继承了一个constructor属性
获取对象属性和原型属性
- Object.keys(): 返回该对象上所有可枚举的属性,不包括原型链
- getOwnProperyNames():返回对象自身的属性(可枚举、不可枚举),不包含原型链上的属性
- obj.hasOwnProperty('xx'): 判断对象是否包含属性(不包含原型链上的属性)
- for ...in 返回所有可枚举的属性 包含原型链
8.练习巩固
题目一
function Person() {
}
var p = new Person()
console.log(Person.prototype); // Object {}
console.log(p.prototype); // undefined 实例没有prototype属性
console.log(p.constructor); // function Person(){} 实例继承了构造函数的原型对象,从而继承了constructor属性,指向构造函数本身
console.log(Person.constructor); // function Function{} 原理同上 每个构造函数都是通过 new Function() 构造而来
console.log({}.constructor); // funciton Object() {} 每个对象都是通过new Object()构造的
console.log(Object.constructor); // function Function(){}
console.log([].constructor);// function Array(){}
题目二
function Person(){
}
var person1=new Person();
person1.__proto__== // Person.prototype
person1.constructor== //Person
Person.__proto__== //Function.prototype
Person.prototype.constructor== //Person
person1.prototype.constructor== // 报错 person1.prototype== undefined
Person.prototype== // person1.__proto__
题目三
function Fun(){
var getName = function(){
console.log(1);
}
return this;
}
Fun.getName = function(){
console.log(2);
}
Fun.prototype.getName = function(){
console.log(3);
}
var getName = function(){
console.log(4);
}
function getName(){
console.log(5);
}
Fun().getName(); //4 Fun函数return this ,this指向Window, 函数表达式提升,之后会被函数声明重新赋值
getName(); //4 知识点:变量提升
new Fun().getName();//3 等价于 var f = new Fun(); f.getName() // 调用原型对象上的属性 方法
new new Fun().getName();//3
参考
- 拆书:3W法的运用
- JavaScript的诞生:www.w3cschool.cn/javascript_…
- JavaScript诞生记:www.ruanyifeng.com/blog/2011/0…
- JavaScript继承机制的设计思想 www.ruanyifeng.com/blog/2011/0…
- JavaScript深入理解从原型到原型链: github.com/mqyqingfeng…
- 什么是原型和原型链:juejin.cn/post/684490…
- 进阶必读:深入理解JavaScript原型 zhuanlan.zhihu.com/p/87667349
- MDN 对象原型:developer.mozilla.org/zh-CN/docs/…
- 获取对象属性和原型属性:blog.csdn.net/weixin_4191…