JavaScript课程——Day15(编程思想、对象的读写、面向对象的创建、面向对象的案例)
1、编程思想
- 面向过程:面向过程思想强调的是步骤,当碰见问题时,思考的是“我该怎么做”,分析出解决问题所需的步骤,一步步的去实现。
- 面向对象:面向对象思想强调的是对象,当碰见问题时,思考的是“我该让谁来做”。这个“谁”其实就是对象。找合适的对象做合适的事情。而对象如何去做(采用什么样的步骤)我们就不关心了,最终把问题解决掉就可以了。
相关概念:
var 对象 = { key1: value1, key2: value2, ... }
1.1、对象:无序的明值对
1.2、对象组成:
-
- 属性:对象的特征描述,静态,名词
- 方法:对象的行为,动态(函数)
1.3、对象的基本特征:封装、继承、多态(了解)
1.4、类和示例
-
- 类:类是对象的类型模板
- 实例:实例是根据类创建的对象
- 面向对象学习,就是学习创建类(模板),利用类生成实例(月饼)
2、对象的读写
var obj = { name: 'zs', age: 3, aa: undefined, job: function () { console.log(this); console.log('前端开发'); } }; // 读 console.log(obj.name); // 点的形式 console.log(obj['age']); // 中括号的形式 obj.job(); var v = obj.job; v(); // ------------------- // 写 // 如果原来存在这个属性,则就是修改,如果不存在,则是添加 obj.name = 'ls'; obj.sex = '男'; console.log(obj); // ---------------------- // 遍历 for (var attr in obj) { console.log(attr, '-----', obj[attr]); } // ----------------------- // in操作符:检查对象有某个属性没,返回布尔值 // 格式:key in 对象 // 区分清楚属性有不有和值没有一点关系 console.log('name' in obj); // true console.log('abc' in obj); // false console.log('aa' in obj); // true // ---------------------- // 删除对象的某个属性 // 格式:delete 对象.属性名; delete obj.aa; delete obj.job; console.log(obj);
3、面向对象的创建
3.1、字面量创建
var obj = { name: 'zs', age: 3, showNmae: function () { console.log(this.name); } } obj.showNmae(); var obj2 = { name: 'ls', age: 5, showNmae: function () { console.log(this.name); } } console.log(obj2.age); obj2.showNmae(); // 不足:适用于单个对象的创建,如果要创建多个对象,会代码冗余
3.2、实例创建
var obj = new Object(); // 实例 obj.name = 'zs'; obj.age = 3; obj.showName = function () { console.log(this.name); } console.log(obj); var obj2 = new Object(); // 实例 obj2.name = 'ls'; obj2.age = 5; obj2.showName = function () { console.log(this.name); } console.log(obj2); // 不足:适用于单个对象的创建,如果要创建多个对象,会代码冗余
3.3、工厂模式
// 工厂模式:归根到底是一个封装函数 function createPerson(name, age) { // 1、准备原料 var obj = new Object(); // 2、加工 obj.name = name; obj.age = age; obj.showNmae = function () { console.log(this.name); } // 3、出厂 return obj; } var o1 = createPerson('zs', 3); console.log(o1); var o2 = createPerson('ls', 5); console.log(o2); // 优点:可以大量创建 // 不足:不能标识出它是由谁创建的
instanceof
// 格式:对象 instanceof 函数 // 作用:运算符,一个对象是否为指定的构造函数的实例,返回布尔值 console.log([] instanceof Array); // true console.log([] instanceof Object); // true 因为Object是我们js世界里的老大,一切对象都是由它创建的 console.log(o1 instanceof createPerson); // false 这是工厂模式的不足,不能标识出由谁创建的 console.log(o1 instanceof Object); // true
3.4、构造函数模式
构造函数的特点:
1、构造函数名首字母大写(为了区分普通函数,不是必须,是约定)
2、构造函数方法没有显示的创建对象(new object())
3、直接将属性和方法赋值给this对象
4、没有return语句,不需要返回对象
5、通过构造函数创建对象,必须使用new运算符(直接调用跟普通函数一样)
function CreatePerson(name, age) { this.name = name; this.age = age; this.showName = function () { console.log('小张'); } }
var p1 = new CreatePerson('zs', 3); console.log(p1); console.log(p1.showName) console.log(p1.name); p1.showName(); var p2 = new CreatePerson('ls', 5); console.log(p2); console.log(p2.showName) // 问题:同样的方法,在每个实例上都要重新生成,耗内存 console.log(p1.showName == p2.showName); // false
以这种方式调用构造函数实际上会经历以下4个步骤
(1)创建一个新对象;
(2)将构造函数的作用域赋给新对象(因此this就指向了这个新对象);
(3)执行构造函数中的代码(为这个新对象添加属性);
(4)返回新对象(隐式返回)。(因此p1就是这个新对象,同时p1也是this)
var o = CreatePerson('zs', 3); // 直接调用就是普通函数调用,则这些属性和方法加给了window,同时没有返回值,所以o是undefined。所以构造函数一定要能过new调用
对象的比较
// 基本类型,基本类型的比较,是值的比较 console.log(5 == 5); // true // 引用类型,引用类型的比较,是地址的比较 var a = []; var b = []; console.log(a == b); // false
3.5、原型创建对象
(1)原型
js每声明一个function,都有prototype原型,prototype原型是函数的一个默认属性,在函数的创建过程中由js编译器自动添加。
也就是说,当生产一个function对象的时候,就有一个原型prototype。原型中存储对象共享的属性和方法。
(2)原型链
当查找对象的属性的时候,先找自身,如果自身没有,则顺着__prototype__找到原型,如果原型也没有,则继续向上找,一直找到Object的原型。这个查找链表就是原型链。
// 原型对象:存储对象公共属性和方法的地方 // 每写一个函数,都会有一个原型对象prototype var arr = new Array(); // Array就是构造函数 console.log(arr); // arr就是实例 console.log(arr.__proto__); // 原型对象,通过实例找到原型 console.log(Array.prototype); // 原型对象,通过构造函数找到原型 console.log(arr.__proto__ == Array.prototype); // true
原型创建对象
function CreatePerson() { } // 每声明一个函数,都会有一个原型对象,原型对象默认有一个constructor的属性,它又指向构造函数 // console.log(CreatePerson.prototype); // CreatePerson的原型 {constructor: ƒ} // console.log(CreatePerson.prototype.constructor); // constructor CreatePerson.prototype.name = 'zs'; CreatePerson.prototype.age = 3; CreatePerson.prototype.showName = function () { console.log('小张'); } // console.log(CreatePerson.prototype); // -------------------------- var p1 = new CreatePerson(); console.log(p1); // console.log(p1.name); // p1.showName() // console.log(p1.constructor); // console.log(p1.toString); var p2 = new CreatePerson(); console.log(p2); console.log(p1.showName == p2.showName); // true // 优点:原型方法完美的解决了构造函数会方法会重复创建的问题 // 不足:不能似参,创建的结果都一样 console.log(p2.abc); // undefined
3.6、混合模式创建对象
// 是我们创建对象的标准方法 // 混合模式:构造函数+原型 // 每个对象独有的写在构造函数里,公有的写在原型上 function CreatePerson(name, age) { this.name = name; this.age = age; } CreatePerson.prototype.showName = function () { console.log(this.name); } var p1 = new CreatePerson('zs', 3); console.log(p1); // console.log(p1.name); // p1.showName(); var p2 = new CreatePerson('ls', 5); console.log(p2);
3.7、动态混合模式
// 动态混合模式 // 好处:代码有一个封装的感觉 // 执地原理:同混合模式 function CreatePerson(name, age) { this.name = name; this.age = age; if (typeof CreatePerson.prototype.showName !== 'function') { CreatePerson.prototype.showName = function () { console.log(this.name); } CreatePerson.prototype.showAge = function () { console.log(this.age); } } } var p1 = new CreatePerson('zs', 3); console.log(p1); var p2 = new CreatePerson('ls', 5); console.log(p2);
4、面向对象的案例
面向对象的选项卡
原则:先写出普通的写法,然后改成面向对象方法
1、普通方法变型
尽量不要出现函数嵌套函数
可以有全局变量
把onload中不是赋值的语句放到单独函数中(init)
2、改成面向对象()
先写构造函数
onload中创建对象,并init调用
全局变量就是属性
函数就是方法
(属性和方法前面,都要加this)
改this指向问题(尽量让this指向对象)
结论:我们改成面向对象之后,感觉是不是更复杂了?确实是这样,确实是复杂了,但是我们这个面向对象特别适合复杂的开发,对于简单的,不太推荐使用面向对象。面对复杂开发时,它特别容易扩展,同时,复用性特别强。上面的例子,多添加几个,就可以发现特别方便复用和扩展。
普通方法实现和变形
<head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> span { display: inline-block; width: 100px; height: 30px; background-color: #ccc; line-height: 30px; text-align: center; color: white; cursor: pointer; } .active { background-color: red; } #box div { width: 330px; height: 150px; border: 2px solid #ccc; font-size: 40px; line-height: 150px; text-align: center; display: none; } </style> <script> // 普通方法实现 // window.onload = function () { // var box = document.getElementById('box'); // var span = box.querySelectorAll('span'); // var div = box.querySelectorAll('div'); // for (var i = 0; i < span.length; i++) { // span[i].index = i; // 自定义下标 // span[i].onclick = function () { // // 1、所有的span去掉样式,所有的div隐藏 // for (var i = 0; i < span.length; i++) { // span[i].className = ''; // div[i].style.display = 'none'; // } // // 2、当前的span加上样式,对应的div显示 // this.className = 'active'; // div[this.index].style.display = 'block'; // } // } // } // --------------------------------------- // 普通方法变形 var box = null; var span = null; var div = null; window.onload = function () { box = document.getElementById('box'); span = box.querySelectorAll('span'); div = box.querySelectorAll('div'); init(); } function init() { for (var i = 0; i < span.length; i++) { span[i].index = i; // 自定义下标 span[i].onclick = change; } } function change() { // 1、所有的span去掉样式,所有的div隐藏 for (var i = 0; i < span.length; i++) { span[i].className = ''; div[i].style.display = 'none'; } // 2、当前的span加上样式,对应的div显示 this.className = 'active'; div[this.index].style.display = 'block'; } </script> </head> <body> <div id="box"> <span class="active">html</span> <span>css</span> <span>js</span> <div style="display: block;">html</div> <div>css</div> <div>js</div> </div> </body>
改成面向对象
<head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> span { display: inline-block; width: 100px; height: 30px; background-color: #ccc; line-height: 30px; text-align: center; color: white; cursor: pointer; } .active { background-color: red; } #box div { width: 330px; height: 150px; border: 2px solid #ccc; font-size: 40px; line-height: 150px; text-align: center; display: none; } </style> <script> // var box = null; // var span = null; // var div = null; // window.onload = function () { // box = document.getElementById('box'); // span = box.querySelectorAll('span'); // div = box.querySelectorAll('div'); // init(); // } // function init() { // for (var i = 0; i < span.length; i++) { // span[i].index = i; // 自定义下标 // span[i].onclick = change; // } // } // function change() { // // 1、所有的span去掉样式,所有的div隐藏 // for (var i = 0; i < span.length; i++) { // span[i].className = ''; // div[i].style.display = 'none'; // } // // 2、当前的span加上样式,对应的div显示 // this.className = 'active'; // div[this.index].style.display = 'block'; // } // ------------------------------------- function Tab() { this.box = document.getElementById('box'); this.span = this.box.querySelectorAll('span'); this.div = this.box.querySelectorAll('div'); } Tab.prototype.init = function () { var that = this; // 对象存给了that for (var i = 0; i < this.span.length; i++) { this.span[i].index = i; // 自定义下标 this.span[i].onclick = function () { // this--span that.change(this); } } } Tab.prototype.change = function (ele) { // 1、所有的span去掉样式,所有的div隐藏 for (var i = 0; i < this.span.length; i++) { this.span[i].className = ''; this.div[i].style.display = 'none'; } // 2、当前的span加上样式,对应的div显示 ele.className = 'active'; this.div[ele.index].style.display = 'block'; } window.onload = function () { var t1 = new Tab(); t1.init(); } </script> </head> <body> <div id="box"> <span class="active">html</span> <span>css</span> <span>js</span> <div style="display: block;">html</div> <div>css</div> <div>js</div> </div> </body>