首页 > 代码库 > js_面向对象编程
js_面向对象编程
主要内容
值类型与引用类型的特征
深拷贝与浅拷贝
对象的动态特性
构造函数的执行过程
异常处理
dom操作(略)
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> </head> <body> </body> <script> var car = { name: ‘法拉利‘, deepCopy: function() { // 假设 copy 已经写好 // 1, 创建一个对象 var temp = {}; // 2, 拷贝属性, 在判断如果是引用类型需要深拷贝 for ( var k in this ) { if ( typeof this[ k ] === ‘object‘ ) { temp[ k ] = this[ k ].deepCopy(); } else { temp[ k ] = this[ k ]; } } // 3, 返回对象 return temp; } }; var p = { name: ‘张三‘, car: car, deepCopy: function () { // 1, 创建一个对象 var temp = {}; // 2, 拷贝属性, 在判断如果是引用类型需要深拷贝 for ( var k in this ) { if ( typeof this[ k ] === ‘object‘ ) { temp[ k ] = this[ k ].deepCopy(); } else { temp[ k ] = this[ k ]; } } // 3, 返回对象 return temp; } } // 需要保证所有的 对象中 都有 copy 方法 那么就可以简化了 // 写一个 deepCopy 函数, 每一个对象都是用 <对象>.deepCopy = deepCopy // 来使得当前对象具有 拷贝的方法, 那么就可以实现深拷贝了 </script> </html>
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> </head> <body> </body> <script> var deepCopy = function () { // 1, 创建一个对象 var temp = {}; // 2, 拷贝属性, 在判断如果是引用类型需要深拷贝 for ( var k in this ) { if ( typeof this[ k ] === ‘object‘ ) { temp[ k ] = this[ k ].deepCopy(); } else { temp[ k ] = this[ k ]; } // temp[ k ] = this[ k ]; } // 3, 返回对象 return temp; } var car = { name: ‘法拉利‘ }; var p = { name: ‘张三‘, age: 19, gender: ‘男‘, car: car }; // 让所有的对象都有 拷贝的 方法 car.deepCopy = deepCopy; p.deepCopy = deepCopy; var newP = p.deepCopy(); p.name = ‘李四‘; p.age = 20; p.gender = ‘女‘; p.car.name = ‘兰博基尼‘; </script> </html>
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> </head> <body> </body> <script> var car = { name: ‘法拉利‘ }; var p = { name: ‘张三‘, age: 19, car: car }; // var pCopy = p; // 这个不是拷贝, 没有对对象做任何拷贝行为 // 浅拷贝的代码实现 // var pCopy = {}; // pCopy.name = p.name; // pCopy.age = p.age; // pCopy.car = p.car; // 深拷贝的代码实现 var pCopy = {}; pCopy.name = p.name; pCopy.age = p.age; pCopy.car = {}; pCopy.car.name = p.car.name; </script> </html>
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> </head> <body> </body> <script> function Person () { this.name = ‘张三‘; this.age = 19; this.gender = ‘男‘; } var p = new Person(); </script> </html>
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> </head> <body> </body> <script> var deepCopy = function () { // 1, 创建一个对象 var temp = {}; // 2, 拷贝属性, 在判断如果是引用类型需要深拷贝 for ( var k in this ) { if ( typeof this[ k ] === ‘object‘ ) { temp[ k ] = this[ k ].deepCopy(); } else { temp[ k ] = this[ k ]; } // temp[ k ] = this[ k ]; } // 3, 返回对象 return temp; } var car = { name: ‘法拉利‘ }; var p = { name: ‘张三‘, age: 19, gender: ‘男‘, car: car }; // 让所有的对象都有 拷贝的 方法 car.deepCopy = deepCopy; p.deepCopy = deepCopy; var newP = p.deepCopy(); p.name = ‘李四‘; p.age = 20; p.gender = ‘女‘; p.car.name = ‘兰博基尼‘; </script> </html>
--------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------
主要内容
DOM操作
原型的概念
原型属性与原型对象的含义
原型继承的概念
如何使用原型
复习
- 值类型与引用类型的特征
- 内存结构( 画图 ).
- 赋值特征: 值类型两个数据, 引用类型两个变量一个数据.
- 参数传递: 值类型: 函数内外两不同; 引用类型: 函数内外变量不同同一对象.
- 深拷贝与浅拷贝
- var o = {}; o[ k ] = p[ k ];
- o[ k ] = {}; o[ k ][ key ] = p[ k ][ key ]
- 让每一个对象都提供一个 deepCopy 的方法
- 注意: 1, 什么是深浅拷贝; 2, 代码实现的方式
- 对象的动态特性
- 如果对象想要属性, 就加上即可
- 如果对象想要方法, 就加上即可
- 如果对象想移除方法或属性, 就 delete 即可
var o = {}; o.name = ‘jim‘; o.sayHello = function () {};
- 构造函数的执行过程
- 按照构造方法的执行过程一一说明其步骤
- 异常处理
- 什么是异常
- 分类: 1, 运行环境多样性; 2, 代码语法错误
- 处理办法: 语法
- 如何抛出: 语法
- DOM 操作
- 绘制 DOM 树: childNodes, attributes
- 从一个中心元素访问其所有的直系亲属元素
- 访问父节点: parentNode
- 访问上一个兄弟节点: previousSibling
- 访问下一个兄弟节点: nextSibling
- 访问第 1 个属性节点: attributes[ 1 ]
- 访问最后一个子节点: lastChild 或 childNodes[ childNodes.length - 1 ]
补充
构造函数的执行过程
- new 创建对象, 然后传递给构造函数的 this
- 在构造函数内部使用对象的动态特性为对象添加成员
构造函数的本质与下面的代码一样
function Person ( o ) {
o.name = ‘jim‘;
o.age = 19;
o.sex = ‘male‘; // 男, female 女
return o;
}
var p = Person( {} );
一开始就是 {}, 然后加属性. 至少要四行代码
var p = {};
p.name = ‘jim‘;
p.age = 19;
p.sex = ‘male‘;
如果有函数, 可以将上面的四句话减为一句话
function createPerson() {
var p = {};
p.name = ‘jim‘;
p.age = 19;
p.sex = ‘male‘;
return p;
}
var per = createPerson();
深浅拷贝
var p1 = {};
p1.name = p.name;
...
p1.car = {};
p1.car.name = p.car.name;
DOM 操作
访问由于树关系( 节点 )
方法与功能
核心内容就是利用 增删改查
查询
就是获取元素
- 标准 DOM API
- doucment.getElementById
- document.getElementsByTagName
- document.getElementsByName
- document.getElementsByClassName
- document.querySelectorAll
- 亲属访问
- 属性获取
- getAttribute
- getAttributeNode
增加
- 创建
- document.createEelement 创建元素节点
- document.createTextNode 创建文本节点
- doucment.createAttribute 属性节点
- innerHTML
- innerText
- cloneNode()
- 加入
- appendChild 追加到结尾处
- innerHTML
- insertBefore 用法?将元素插入到某一个元素的前面
父元素.insertBefore( 新元素, 旧元素 ); // 将 新元素 插入到 旧元素 的前面
- 其他
- style 的操作
- setAttribute( 属性名, 属性值 )
删除
- 删除元素
- removeChild
- removeAttributeNode
修改
- 修改节点
- 删除节点再加入
- 修改样式
- style.xxx = vvv;
- setAttribute
- 修改文本
- innerHTML
- innerText
- 节点操作
- nodeValue
- 修改属性
- .xxx = vvv
- setAttribute
面试题
- js 是什么语言
- 基于对象: 没有对象什么事儿都所不了
- 脚本语言: 不严格的定义, 写出来解释执行的语言. 和编译型语言对象( c, c++, java, c# ).
- 基于原型: 和继承有关
- 出现 undefiend
- 访问数组或对象不存在的成员
- 定义变量为赋值
- 函数没有返回值, 或 return 后没有数据
- 对数组元素去重( * )
- 利用一个新数组存数据
- 遍历旧数组, 然后在新数组中找是否已存在该数据
- indexOf != -1
- DOM 操作
- 先获取元素, 设置或修改属性或子节点
原型 prototype
js 的对象比较
由于 js 是解释执行的语言, 那么再代码中出现函数与对象如果重复执行, 会创建多个副本
- 在代码中重复执行的代码容易出现重复的对象
- 创建一个 Person 构造函数, 以创建 对象. 要求有 name, age, gender 和 sayHello
- 代码出现的错误
// 1 function Person() { var o = {}; o.name = ... return o; } // 2 function Person () { name: .... age: .... ... }
- 传统的构造方法的定义方式会影响性能, 容易造成多个对象有多个方法副本. 应该讲方法单独抽取出来. 让所有的对象共享该方法.
- 可以考虑将方法全部放到外面但是有安全隐患
- 在开发中会引入各种框架或库. 自定义的成员越多, 出现命名冲突的几率越大
- 可能在开发中会有多个构造函数. 每一个构造函数应该有多个方法. 那么就会变得不容易维护.
- 任意一个对象都会默认的链接到它的原型中
- 创建一个函数. 会附带的创建一个特殊的对象. 该对象使用 函数.prototype 引用. 称其为函数的原型属性.
- 每一个由该函数作为构造函数创建的对象, 都会默认的连接到该对象上.
- 在该对象访问某一个方法或属性的时候, 如果该对象中没有, 就会到这个神秘对象中去查找.
传统构造函数的问题
function Foo() {
this.sayHello = function () {
}
}
- 由于对象是调用
new Foo()
所创建出来的. 因此每一个对象在创建的时候, 函数 sayHello 都会被创建一次 - 那么每一个对象都含有一个独立的, 不同的, 但是功能逻辑一样的函数. 比如:
{} == {}
- 在代码中方法就会消耗性能. 最典型的资源就是内存.
- 这里最好的办法就是将函数体放在构造函数之外. 那么在构造函数中只需要引用该函数即可
function sayHello () {} function Foo () { this.say = sayHello; }
- 会在开发中变得困难: 引入框架危险, 代码繁冗不好维护. 解决办法就是外面的函数如果不占用名字. 而且在函数旗下就好了.
- 每一个函数在定义的时候, 有一个神秘对象被创建出来.
- 每一个由构造函数创建的对象都会默认的连接到该神秘对象上.
var f1 = new Foo(); var f2 = new Foo(); f1.sayHello(); // 如果 f1 没有 sayHello, 那么就会在 Foo.prototype 中去找 f2.sayGoodBye(); // 如果 f2 没有改方法, 那么就会在 Foo.prototype 中去找
- 由构造函数创建出来的众多对象共享一个对象, 就是 构造函数.prototype
-
只需要将共享的东西, 重复会多占用内存的东西放到 构造函数.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
- 练习: 写一个构造函数 Student, 要求有 name, age, gender, sayHello, study. 要求构造函数带参数.
常见错误
- 写 构造函数.prototype 的时候, 将属性也加到里面.
function Person() {} Person.prototype.name = ‘张三‘; var p = new Person();
-
赋值的错误
function Person() {} Person.prototype.name = ‘张三‘; var p1 = new Person(); var p2 = new Person(); p1.name = ‘李四‘; console.log( p1.name ); console.log( p2.name ); // 如果是访问数据, 当前对象中如果没有该数据就到构造函数的原型属性中去找 // 如果是写数据, 当对象中有该数据的时候, 就是修改值; 如果对象没有该数据, 那么就添加值
原型相关的概念
- 关于面向对象的概念
- 类 class: 在 js 中就是构造函数
- 在传统的面向对象语言中, 使用一个叫做类的东西定义模板. 然后使用模板创建对象.
- 在构造方法中也具有类似的功能. 因此称其为类
// 在 java 中, 最小的代码单位是 类 class Program { // 成员 }
- 实例 ( instance ) 与对象 ( object )
- 实例一般是指某一个构造函数创建出来的对象. 我们成为 xxx 构造函数的实例
- 实例就是对象. 对象是一个泛称.
- 实例与对象是一个近义词
- 键值对与属性和方法
- 在 js 中键值对的集合称为对象
- 如果值为数据( 非函数 ), 就称该键值对为属性 property
- 如果值为函数( 方法 ), 就称该键值对为方法 method
- 父类与子类
- 传统的面向对象语言中使用类来实现继承. 那么就有父类, 子类的概念
- 父类又称为基类, 子类又称为派生类
- 在 js 中常常称为父对象, 子对象. 基对象, 派生对象.
- 类 class: 在 js 中就是构造函数
- 原型相关的概念
- 神秘对象针对构造函数称为 "原型属性"
- 神秘对象就是构造函数的原型属性
- 简称原型
- 神秘对象与构造函数所创建出来的对象也有一定关系
- 关系是什么
- 神秘对象针对构造函数创建出来的对象称为 "原型对象"
- 简称原型
- 对象继承自其原型
- 构造函数创建的对象 继承自 构造函数的原型属性
- 构造函数创建的对象 继承自 该对象的原型对象
- 构造函数所创建出来的对象与构造函数的原型属性表示的对象是两个不同的对象
- 原型中的成员, 可以直接被实例对象所使用
- 也就是说实例对象直接 "含有" 原型中的成员
- 因此 实例对象 继承自 原型
- 这样的继承就是 "原型继承"
- 神秘对象针对构造函数称为 "原型属性"
- 一些问题
- {} 构造函数是什么?
- 凡是字面量的对象都有构造函数
- {} Object
- [] Array
- /./ RegExp
- function ... Function
如何使用原型
为什么使用原型?
- 利用对象的动态特性
- 构造函数.prototype.XXX = vvvv;
- 利用直接替换
Student.prototype = { sayHello: function () {}, study: function () {} };
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> </head> <body> </body> <script> function Person () {} var p = new Person(); // 1. 执行 new 创建 对象 // 2. 执行构造函数 Person 初始化对象( 给对象添加属性 ) // 3. 赋值给 变量 p // 现在 p 表示的对象 默认连接到 Person.prototype Person.prototype.good = function () { alert ( ‘好‘ ); }; p.good(); </script> </html>
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> </head> <body> </body> <script> 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(); p2.func(); </script> </html>
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
答疑DOM内容
__proto__
原型继承的概念
混合式继承
绘制构造原型实例三角形
属性搜索原则
静态成员与实例成员
1. 复习 删除子元素 // 从 node 中删除 一个 div node.removeChild( div ); // 如果 div 变量不存在 var div = document.getElementById( ‘...‘ ); node.removeChild( div ); // 假设 node 节点中只有一个元素 div node.innerHTML = ‘‘; 删除属性 var attrNode = node.getAttributeNode( ‘属性名‘ ); node.removeAttributeNode( attrNode ); // removeAttribute 是什么意思呢? node.removeAttribute( ‘属性名‘ ); // getAttributeNode // getAttribute 获取属性值 var attrNode = node.getAttributeNode( ‘属性名‘ ); attrNode.nodeValue; // 简化 node.getAttribute( ‘属性名‘ ); 作业 画 DOM 树 table 表格案例 <html> <head> <title>文本</title> <meta charset="utf-8" /> </head> <body> <div> 111111<span style="color: red;">22222</span>3333</div> </body> </html> 2. proto 以前要访问原型, 必须使用构造函数来实现. 无法直接使用实例对象来访问原型. 火狐最早引入属性 __proto__ 表示使用实例对象引用原型. 但是早期是非标准的. 通过该属性可以允许使用实例对象直接访问原型 function Person() {} // 神秘对象就是 Person.prototype // 那么只有使用 构造函数 才可以访问它 var o = new Person(); // 以前不能直接使用 o 来访问神秘对象 // 现在有了 __proto__ 后 // o.__proto__ 也可以直接访问神秘对象( 两个下划线 ) // 那么 o.__proto__ === Person.prototype 神秘对象中默认都有一个属性 constructor, 翻译为 构造器. 表示该原型是与什么构造函数练习起来的. __proto__ 有什么用? 可以访问原型 由于在开发中除非特殊要求, 不要使用实例去修改原型的成员. 因此属性开发时使用较少 但是再调试过程中非常方便, 可以轻易的访问原型进行查看成员 如果在 早期的浏览器中使用 实例需要访问原型如何处理? 可以使用实例对象访问 构造器, 然后使用构造器访问原型 var o = new Person(); o.constructor.prototype 如果给实例继承自原型的属性赋值 function Foo() {} Foo.prototype.name = ‘test‘; var o1 = new Foo(); var o2 = new Foo(); o1.name = ‘张三‘; // 不是修改原型中的 name 而是自己增加了一个 name 属性 console.log( o1.name + ‘, ‘ + o2.name ); 3. 继承 最简单的继承就是 将别的对象的属性强加到 我的 身上, 那么我就有这个成员了. 利用原型也可以实现继承, 不需要在我的身上添加任何成员, 只要原型有了, 我就有了. 结论: 将属性, 方法等成员利用 混入的办法, 加到构造函数的原型上, 那么构造函数的实例就都具有该方法了. 3.1. 混合式继承复杂描述 new DivTag() 用来 创建 div 对象 appendTo 加到某元素上 扩展 img p span a ... 无论方法怎么写, 方法是谁调用的, this 就是谁 4. 面试题 将字符串中 >, <, &, TM, 空格, ... 逻辑中断 // foo = foo || bar; // || 逻辑或, 左边如果为真, 那么整个表达式的值 就是 左边的值 // || 如果 左边的值为 假, 那么整个表达式的值就是右边的值 // 如果考虑函数参数的时候, 一般使用 该 方法来兼容处理参数 div.onclick = function ( e ) { e = e || window.event; }; 变量名提升 5. 中午复习 function Person() {} var p1 = new Person(); var p2 = new Person.prototype.constructor(); 6. 细节 使用点语法给原型添加成员与使用直接替换修改原型对象有什么区别? 原型指向发生了变化 构造函数所创建的对象所继承的原型不同 新增的对象默认是没有 constructor 属性 注意: 在使用替换的方式修改原型的时候, 一般都会添加 constructor 属性. function Person() {} Person.prototype = { constructor: Person }; // 拆解 function Person() {} var o = {}; o.costructor = Person; // 属性中就存储着函数的地址 Person.prototype = o; Person = 123; 7. 静态成员与实例成员的概念 也是从 面向对象的 变成语言中引入的 静态成员表示的是 静态方法和 静态属性的概念. 所谓的静态, 就是由构造函数所提供的. 实例成员表示的是 实例方法 和 实例属性. 所谓的实例就是由构造函数所创建的对象. 一般工具型方法都有静态成员提供, 一般与实例对象有关的方法由实例成员表示. 8. 三角形绘图 练习: // 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(); 9. 原型与继承小结 什么是原型? 如何使用原型? 利用点添加成员 直接替换添加成员 什么是原型继承? 实例对象是继承自 原型对象 的 一般的开发方式: 属性交给构造函数, 方法交给原型 如何实现? 混合继承方式: 利用混入的办法给原型添加成员, 那么实例对象就可以继承指定的方法和属性 直接替换原型对象 // 例如要实现一个自定义的集合( 数组 ) // 弄一个类型 ItcastCollection function ItcastCollection () {} // 要提供数组的方法为其添加成员 ItcastCollection.prototype = []; 10. 从案例中引出的问题 什么时候会得到 undefined. 我们介绍的是在实例中没有找到, 就去原型中找, 但是原型中没有怎么办? 10.1. 属性搜索原则 原型链 属性搜索原则 所谓的属性搜索原则, 就是对象在访问属性与方法的时候, 首先在当前对象中查找 如果当前对象中存储在属性或方法, 停止查找, 直接使用该属性与方法 如果对象没有改成员, 那么再其原型对象中查找 如果原型对象含有该成员, 那么停止查找, 直接使用 如果原型还没有, 就到原型的原型中查找 如此往复, 直到直到 Object.prototype 还没有, 那么就返回 undefied. 如果是调用方法就包错, 该 xxxx 不是一个函数 11. 作业 构造函数的执行过程分析( 要求使用文字将每一句话的执行描述出来 ) // 1 function Person() { this.name = ‘jim‘; this.sayHello = function () { console.log( ‘hello‘ ); } } var p = new Person(); // 2 function Student( name, age ) { this.name = name; this.age = age; } Student.prototype = { sayHello: function () { console.log( ‘hello‘ ); } }; var p = new Student( ‘张三‘, 19 ); 自己定义 DivTag 与 PTag 构造函数, 完成 元素的添加. 要求使用原型继承 绘制内存逻辑图 // 1 function Person() { this.name = ‘张三‘; this.sayHello = function () { } } var p = new Person(); var q = new Person(); // 2 function Person() { this.name = ‘张三‘; } Person.prototype.sayHello = function () { } var p = new Person(); var q = new Person(); // 3 function Person() { this.name = ‘张三‘; } Person.prototype = { sayHello: function () { } }; var p = new Person(); var q = new Person(); 复述属性搜索原则
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> </head> <body> </body> <script> var data = http://www.mamicode.com/[>
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> </head> <body> </body> <script> function Person ( name ) { this.name = name; } var p = new Person( ‘jim‘ ); </script> </html>
【作者】:轻轻走143 |
【出处】:http://www.cnblogs.com/qingqingzou-143/ |
【声明】:所有博文标题后加(share)的表示收集的他人优秀文章,其余的则为原创。欢迎转载,但请在显要位置显示本文链接,并保留本段声明,否则追究法律责任,谢谢! |
js_面向对象编程