关于javascript中的面向对象
经常会听到对象和面向对象这个词。关于面向对象的一些理解,可能会比较肤浅,平时在书也看到一些相习的内容,我把整理的一些笔记分享给大家。首先,从JS的语言特征说起,js 是什么语言?首先,是基于对象的语言,如果没有对象这个概念,那么什么事情都做不了;再者,是一种脚本语言,是写出来就可以执行解析的一种语言,区别于编译语言( c, c++, java, c# );最后,就是继承性,什么是继承性呢?通俗的说来就是把别人东西,拿过来做为自己的,为自己所用,如特征,属性,方法等,继承性:(for(var key in o1){o2[key]=01[key]}),是利用for in结构的一种很简单与常见的继承性方法之一。
由于 js 是解释执行的语言, 那么再代码中出现函数与对象如果重复执行, 会创建多个副本,
1. 在代码中重复执行的代码容易出现重复的对象 2. 创建一个 Person 构造函数, 以创建 对象. 要求有 name, age, gender 和 sayHello 3. 代码出现的错误 ``` // 1 function Person() { var o = {}; o.name = ... return o; } // 2 function Person () { name: .... age: .... ... } ``` 4. 传统的构造方法的定义方式会影响性能, 容易造成多个对象有多个方法副本. 应该讲方法单独抽取出来. 让所有的对象共享该方法. 5. 可以考虑将方法全部放到外面但是有安全隐患 * 在开发中会引入各种框架或库. 自定义的成员越多, 出现命名冲突的几率越大 * 可能在开发中会有多个构造函数. 每一个构造函数应该有多个方法. 那么就会变得不容易维护. 6. 任意一个对象都会默认的链接到它的原型中 * 创建一个函数. 会附带的创建一个特殊的对象. 该对象使用 函数.prototype 引用. 称其为函数的原型属性. * 每一个由该函数作为构造函数创建的对象, 都会默认的连接到该对象上. * 在该对象访问某一个方法或属性的时候, 如果该对象中没有, 就会到这个神秘对象中去查找.
传统构造函数的问题
``` function Foo() { this.sayHello = function () { } } ``` 1. 由于对象是调用 `new Foo()` 所创建出来的. 因此每一个对象在创建的时候, 函数 sayHello 都会被创建一次 2. 那么每一个对象都含有一个独立的, 不同的, 但是功能逻辑一样的函数. 比如: `{} == {}` 3. 在代码中方法就会消耗性能. 最典型的资源就是内存. 4. 这里最好的办法就是将函数体放在构造函数之外. 那么在构造函数中只需要引用该函数即可 ``` function sayHello () {} function Foo () { this.say = sayHello; } ``` 5. 会在开发中变得困难: 引入框架危险, 代码繁冗不好维护. 解决办法就是外面的函数如果不占用名字. 而且在函数旗下就好了. 6. 每一个函数在定义的时候, 有一个神秘对象被创建出来. 7. 每一个由构造函数创建的对象都会默认的连接到该神秘对象上. ``` var f1 = new Foo(); var f2 = new Foo(); f1.sayHello(); // 如果 f1 没有 sayHello, 那么就会在 Foo.prototype 中去找 f2.sayGoodBye(); // 如果 f2 没有改方法, 那么就会在 Foo.prototype 中去找 ``` 8. 由构造函数创建出来的众多对象共享一个对象, 就是 构造函数.prototype 9. 只需要将共享的东西, 重复会多占用内存的东西放到 构造函数.prototype 中, 那么所有的对象就可以共享了. ``` function Foo() {} Foo.prototype.sayHello = function () { console.log( ... ); }; var f1 = new Foo(); f1.sayHello(); var f2 = new Foo(); f2.sayHello(); f1.sayHello === f2.sayHello ```
常见错误 1. 写 构造函数.prototype 的时候, 将属性也加到里面. ``` function Person() {} Person.prototype.name = '张三'; var p = new Person(); ``` 2. 赋值的错误 ``` function Person() {} Person.prototype.name = '张三'; var p1 = new Person(); var p2 = new Person(); p1.name = '李四'; console.log( p1.name ); console.log( p2.name ); // 如果是访问数据, 当前对象中如果没有该数据就到构造函数的原型属性中去找 // 如果是写数据, 当对象中有该数据的时候, 就是修改值; 如果对象没有该数据, 那么就添加值 ``` 原型相关的概念 1. 关于面向对象的概念 * 类 class: 在 js 中就是构造函数 * 在传统的面向对象语言中, 使用一个叫做类的东西定义模板. 然后使用模板创建对象. * 在构造方法中也具有类似的功能. 因此称其为类 ``` // 在 java 中, 最小的代码单位是 类 class Program { // 成员 } ``` * 实例 ( instance ) 与对象 ( object ) * 实例一般是指某一个构造函数创建出来的对象. 我们成为 xxx 构造函数的实例 * 实例就是对象. 对象是一个泛称. * 实例与对象是一个近义词 * 键值对与属性和方法 * 在 js 中键值对的集合称为对象 * 如果值为数据( 非函数 ), 就称该键值对为属性 property * 如果值为函数( 方法 ), 就称该键值对为方法 method * 父类与子类 * 传统的面向对象语言中使用类来实现继承. 那么就有父类, 子类的概念 * 父类又称为基类, 子类又称为派生类 * 在 js 中常常称为父对象, 子对象. 基对象, 派生对象. 2. 原型相关的概念 * 神秘对象针对构造函数称为 "原型属性" * 神秘对象就是构造函数的原型属性 * 简称原型 * 神秘对象与构造函数所创建出来的对象也有一定关系 * 关系是什么 * 神秘对象针对构造函数创建出来的对象称为 "原型对象" * 简称原型 * 对象继承自其原型 * 构造函数创建的对象 继承自 构造函数的原型属性 * 构造函数创建的对象 继承自 该对象的原型对象 * 构造函数所创建出来的对象与构造函数的原型属性表示的对象是两个不同的对象 * 原型中的成员, 可以直接被实例对象所使用 * 也就是说实例对象直接 "含有" 原型中的成员 * 因此 实例对象 继承自 原型 * 这样的继承就是 "原型继承"
如何使用原型 1. 利用对象的动态特性 * 构造函数.prototype.XXX = vvvv; 2. 利用直接替换 ``` Student.prototype = { sayHello: function () {}, study: function () {} }; ``` 执行代码:
function Person () { } | |
Person.prototype.func = function () { | |
console.log( '11111' ); | |
}; | |
var p1 = new Person(); | |
Person.prototype = { | |
func: function () { | |
console.log( '22222' ); | |
} | |
}; | |
var p2 = new Person(); | |
p1.func(); //11111 | |
p2.func();//22222 | |