Chen Haonan's Notes


  • 首页

  • 标签

  • 归档

JavaScript中的继承

发表于 2017-07-13

JavaScript中的继承

JavaScript中的继承主要使用原型链实现
1.原型继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var SuperType = function () {
this.property = true
}
SuperType.prototype.getSuperValue = function () {
return this.property
}
var SubType = function () {
this.property = false
}
SubType.prototype = new SuperType()

var instance = new SubType()

instance.getSuperValue()
// false
instance instanceof Object
// true
instance instanceof SuperType
// true
instance instanceof SuberType
// true

优点:实现了继承
缺点:实例之间的属性共享问题
2.借用构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
var Person = function (name) {
this.name = name
this.friends = ['tom', 'petter', 'li']
}
Person.prototype.sayName = function () {
console.log(this.name)
}
var Parent = function (name) {
Person.call(this, name)
}

var instance0 = new Parent('tony')
var instance1 = new Parent('bruce')

instance0.friends
// ['tom', 'petter', 'li']
instance1.friends
// ['tom', 'petter', 'li']

instance0.friends.push('bob')
instance1.friends.push('bill')

instance0.friends
// ['tom', 'petter', 'li', 'bob']
instance1.friends
// ['tom', 'petter', 'li', 'bill']

优点:实例之间的属性可以独立
缺点:实例之间的方法无法复用

3.组合继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var Person = function (name) {
this.name = name
this.friends = ['tom', 'petter', 'li']
}
Person.prototype.sayName = function () {
console.log(this.name)
}
var Parent = function (name, age) {
Person.call(this, name)
this.age = age
}

Parent.prototype = new Person()
Parent.prototype.constructor = Person
Parent.prototype.sayAge = function () {
console.log(this.age)
}

优点:不同的实例拥有各自的属性和相同的方法
缺点:调用两次构造函数

4.原型式继承
这是一种借助已有对象来进行继承的方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function object (o) {
function F () {}
F.prototype = o
return new F()
}
var person = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"]
}
var anotherPerson = object(person)
var yetAnotherPerson = object(person)

anotherPerson.name = "Greg"
anotherPerson.friends.push("Rob")
yetAnotherPerson.name = "Linda"
yetAnotherPerson.friends.push("Barbie")
person.friends
// Shelby,Court,Van,Rob,Barbie

ECMAScript 5 新增 Object.create() 规范化原型式继承
优点:使用方便
缺点:使用属性的值会共享

5.寄生式继承
寄生式继承和寄生构造函数一样,都是在函数的内部对实例进行增强

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var createAnother = function (original) {
var clone = Object.create(original)
clone.sayHello = function () {
console.log('hello')
}
return clone
}
var person = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"]
};
var anotherPerson = createAnother(person)
anotherPerson.sayHello()
// hello

6.寄生组合继承
组合继承存在调用两次父类的构造函数问题这会时父类的原型上创建出多余的属性 解决的方案是 我们只需要父类原型一个拷贝 没有必要调用父类的构造方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function inheritPrototype(subType, superType){
var prototype = object(superType.prototype)
prototype.constructor = subType
subType.prototype = prototype
}
function SuperType(name){
this.name = name
this.colors = ["red", "blue", "green"]
}
SuperType.prototype.sayName = function(){
alert(this.name)
}
function SubType(name, age){
SuperType.call(this, name)
this.age = age
}
inheritPrototype(SubType, SuperType)
SubType.prototype.sayAge = function(){
console.log(this.age)
}

优点: 完美了

以上

JavaScript中对象的创建方式

发表于 2017-07-08

JavaScript中对象的创建方式有两种:

1.new关键字

1
2
3
4
5
6
var person = new Object()
person.name = 'chenhaonan'
person.age = 23
person.sayName = function () {
console.log(this.name)
}

2.对象字面量

1
2
3
4
5
6
7
var person = {
name: 'chenhaonan',
age: 18,
sayName: function () {
console.log(this.name)
}
}

在一般情况下使用的是对象字面量的创建方式

这只是针对单个对象的创建 如果我们需要多次使用一类相同的对象如何去创建呢?每次都写一个字面量工作量太高,而且容易出错。在ES6之前JavaScript还没有引入类(class)的概念,所以我们必须使用一些技巧来模拟这个创建的过程

1.工厂模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var createPerson = function (name, age) {
var o = {}
o.name = name
o.age = age
o.sayName: function () {
console.log(this.name)
}
return o
}
var person = createPerson('chenhaonan', 23')
console.log(person.name)
// chenhaonan
console.log(person.age)
// 23

优点:将对象的创建过程封装成一个函数降低了代码的复杂性
缺点:返回的对象无法识别 没有特定的类型 不知道是一个person对象

2.构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var Person = function (name, age) {
this.name = name
this.age = age
this.sayName = function () {
console.log(this.name)
}
}
var person = new Person()
console.log(person.name)
// chenhaonan
console.log(person.age)
// 23
console.log(person of Person)
// true

优点:可以识别对象的类型
缺点:对象无法共享方法 每个创建的对象属性的不同的 但是方法是一致的 然而我们在每个对象创建时都创建了一个新的方法

3.原型模式

1
2
3
4
5
6
7
var Person = function () {
}
Person.prototype.name = 'chenhaonan'
Person.prototype.age = '23'
Person.prototype.sayName = function () {
console.log(this.name)
}

优点:对象的属性和方法全都可以共享
缺点:对象的部分属性是个性化的不应该被共享结合之前的构造函数模式我们得到了最靠谱的构造原型模式

4.构造原型模式

1
2
3
4
5
6
7
var Person = function (name, age) {
this.name = name
this.age = age
Person.prototype.sayName = function () {
console.log(this.name)
}
}

优点:个性化的属性互不影响 公共的方法对象共享 但是我们每次创建新的对象时 不论Person.prototype上是否已经存在sayName方法了 都会重新创建一个新的 所以

5.动态原型

1
2
3
4
5
6
7
8
9
var Person = function (name, age) {
this.name = name
this.age = age
if (typeof this.sayName !== 'function') {
Person.prototype.sayName = function () {
console.log(this.name)
}
}
}

优点:如果sayName方法已经存在 则不会多次创建

以上

JavaScirpt中的内存分配

发表于 2017-06-15

内存的分类

内存主要分为两种:栈内存和堆内存

栈内存

栈内存用来控制程序的执行流程
就Java来说 程序的入口是main方法 首先将main压入栈内 执行到下一个方法foo时 将foo压入栈中 foo方法调用bar方法时 将bar压入栈中 然后按bar -> foo -> main 的顺序依次把它们弹出 整个程序执行完毕

JavaScript也是这样子的 只不过有一点区别 就是JavaScript不需要显示的指定main方法 所有的JavaScript代码默认都是在隐式的main方法里 这可能会让大家不习惯 这种从上到下像面条一样的代码

语言本身给我们带来的特性是没有好坏的 特性本身是用来解决问题的 如何去使用这些特性就要靠我们自己去选择了
我在写JavaScript会创建一个main 方法来作为我的主函数 整个在代码的流程都在这里执行 这样整个逻辑就非常清晰了

栈内存还可以存基本的数据类型和引用类型的地址 当程序需要操作基本类型的时候就直接把栈中的东西拿出来就可以了 操作引用类型时先把地址拿回来 然后根据这个地址去堆内存中找到对应的对象

这些变量是存在作用域的 它们随着函数push stack 被创建 并且在 pop stack的时候被销毁

堆内存

堆内存用来存引用类型 不像栈内存 堆内存就像一个院子 没有顺序

栈内存是静态的 而堆内存是动态的

内存的生命周期

  1. 分配
  2. 使用
  3. 释放

在c语言中提供了对内存进行管理的接口
而在JavaScript中不需要程序员直接操作内存 因为JavaScript提供了内置的垃圾回收机制 当内存不需要的时候就会自动的回收

如何判断那些内存可以被回收

  1. 引用计数
    这是最简单的垃圾收集算法。对象如果没有其他对象引用到它。对象将被垃圾回收机制回收。
    该算法有个限制:无法处理循环引用
    1
    2
    3
    4
    5
    6
    var div;
    window.onload = function(){
    div = document.getElementById("myDivElement");
    div.circularReference = div;
    div.lotsOfData = new Array(10000).join("*");
    };

myDivElement 一直存在一个自身的引用 即使他在DOM树中移除了 该内存也不会被释放

  1. 标记清除

假定设置一个叫做根(root)的对象(在Javascript里,根是全局对象)。定期的,垃圾回收器将从根开始,找所有从根开始引用的对象,然后找这些对象引用的对象……从根开始,垃圾回收器将找到所有可以获得的对象和所有不能获得的对象 这个算法解决了循环引用的问题。

具体的触发垃圾回收的情况又很复杂这里就不再继续研究了。
以上就是我对内存的理解。

参考
MDN 内存管理

12

Chen Haonan

write something

13 日志
5 标签
© 2020 Chen Haonan
由 Hexo 强力驱动
|
主题 — NexT.Muse v5.1.4