面向对象day04
复习
1. 删除子元素
// 从 node 中删除 一个 div
node.removeChild( div );
// 如果 div 变量不存在
var div = document.getElementById( '...' );
node.removeChild( div );
// 假设 node 节点中只有一个元素 div
node.innerHTML = '';
2. 删除属性
var attrNode = node.getAttributeNode( '属性名' );
node.removeAttributeNode( attrNode );
// removeAttribute 是什么意思呢?
node.removeAttribute( '属性名' );
// getAttributeNode
// getAttribute
```
3. 获取属性值
```
var attrNode = node.getAttributeNode( '属性名' );
attrNode.nodeValue;
// 简化
node.getAttribute( '属性名' );
```
4. 作业
* 画 DOM 树
* table 表格案例
```
<html>
<head>
<title>文本</title>
<meta charset="utf-8" />
</head>
<body>
<div>
111111<span style="color: red;">22222</span>3333</div>
</body>
</html>
```
# __proto__
以前要访问原型, 必须使用构造函数来实现. 无法直接使用实例对象来访问原型.
火狐最早引入属性 `__proto__` 表示使用实例对象引用原型. 但是早期是非标准的.
通过该属性可以允许使用实例对象直接访问原型
```
function Person() {}
// 神秘对象就是 Person.prototype
// 那么只有使用 构造函数 才可以访问它
var o = new Person();
// 以前不能直接使用 o 来访问神秘对象
// 现在有了 __proto__ 后
// o.__proto__ 也可以直接访问神秘对象( 两个下划线 )
// 那么 o.__proto__ === Person.prototype
```
1. 神秘对象中默认都有一个属性 `constructor`, 翻译为 构造器. 表示该原型是与什么构造函数练习起来的.
2. `__proto__` 有什么用?
* 可以访问原型
* 由于在开发中除非特殊要求, 不要使用实例去修改原型的成员. 因此属性开发时使用较少
* 但是再调试过程中非常方便, 可以轻易的访问原型进行查看成员
3. 如果在 早期的浏览器中使用 实例需要访问原型如何处理?
* 可以使用实例对象访问 构造器, 然后使用构造器访问原型
```
var o = new Person();
o.constructor.prototype
```
4. 如果给实例继承自原型的属性赋值
```
function Foo() {}
Foo.prototype.name = 'test';
var o1 = new Foo();
var o2 = new Foo();
o1.name = '张三'; // 不是修改原型中的 name 而是自己增加了一个 name 属性
console.log( o1.name + ', ' + o2.name );
```
# 继承
1. 最简单的继承就是 将别的对象的属性强加到 我的 身上, 那么我就有这个成员了.
2. 利用原型也可以实现继承, 不需要在我的身上添加任何成员, 只要原型有了, 我就有了.
结论:
将属性, 方法等成员利用 混入的办法, 加到构造函数的原型上, 那么构造函数的实例就都具有该方法了.
## 混合式继承复杂描述
1. new DivTag() 用来 创建 div 对象
2. appendTo 加到某元素上
3. 扩展
* img
* p
* span
* a
* ...
4. 无论方法怎么写, 方法是谁调用的, this 就是谁
# 细节
使用点语法给原型添加成员与使用直接替换修改原型对象有什么区别?
1. 原型指向发生了变化
2. 构造函数所创建的对象所继承的原型不同
3. 新增的对象默认是没有 constructor 属性
注意: 在使用替换的方式修改原型的时候, 一般都会添加 constructor 属性.
```
function Person() {}
Person.prototype = {
constructor: Person
};
// 拆解
function Person() {}
var o = {};
o.costructor = Person; // 属性中就存储着函数的地址
Person.prototype = o;
Person = 123;
```
# 静态成员与实例成员的概念
也是从 面向对象的 变成语言中引入的
1. 静态成员表示的是 静态方法和 静态属性的概念. 所谓的静态, 就是由构造函数所提供的.
2. 实例成员表示的是 实例方法 和 实例属性. 所谓的实例就是由构造函数所创建的对象.
一般工具型方法都有静态成员提供, 一般与实例对象有关的方法由实例成员表示.
# 三角形绘图
练习:
```
// 1
function Person() {
this.name = '张三';
this.sayHello = function () {
}
}
var p = new Person();
// 2
function Person() {
this.name = '张三';
}
Person.prototype.sayHello = function () {
}
var p = new Person();
// 3
function Person() {
this.name = '张三';
}
Person.prototype = {
sayHello: function () {
}
};
var p = new Person();
```
```
// 例如要实现一个自定义的集合( 数组 )
// 弄一个类型 ItcastCollection
function ItcastCollection () {}
// 要提供数组的方法为其添加成员
ItcastCollection.prototype = [];
```
# 从案例中引出的问题
1. 什么时候会得到 undefined.
2. 我们介绍的是在实例中没有找到, 就去原型中找, 但是原型中没有怎么办?
## 属性搜索原则
1. 原型链
2. 属性搜索原则
* 所谓的属性搜索原则, 就是对象在访问属性与方法的时候, 首先在当前对象中查找
* 如果当前对象中存储在属性或方法, 停止查找, 直接使用该属性与方法
* 如果对象没有改成员, 那么再其原型对象中查找
* 如果原型对象含有该成员, 那么停止查找, 直接使用
* 如果原型还没有, 就到原型的原型中查找
* 如此往复, 直到直到 Object.prototype 还没有, 那么就返回 undefied.
* 如果是调用方法就包错, 该 xxxx 不是一个函数