JavaScript笔记(三)

JavaScript笔记(三)

  • 三、面向对象的JavaScript
    • 1. 对象应用
      • 1.1. 声明和实例化
      • 1.2. 早绑定和晚绑定
    • 2. 对象作用域
      • 2.1. 公用、私有和受保护作用域
      • 2.2. 静态变量/方法
      • 2.3. 关键字 this
    • 3. 定义类和对象
      • 3.1 工厂方式
        • 原始方式
        • 工厂方式
        • 为函数传递参数
        • 在工厂函数外定义对象的方法
      • 3.2. 构造函数方式
      • 3.3. 原型方式(prototype 属性)
      • 3.4. 混合的构造函数/原型方式
      • 3.5. 动态原型方法
      • 3.6. 建议方式
  • 四、DOM(文档对象模型)
    • 1. DOM模型概述
    • 2. DOM树和节点
    • 3. Document 对象
    • 4. 访问HTML元素
      • 4.1. 根据节点访问HTML元素(getElement(s)By)
      • 4.2. 根据CSS选择器访问HTML元素(querySelector(All)())
      • 4.3. 利用节点关系访问HTML元素(有坑)
      • 4.4. 访问列表框、下拉菜单选项
        • select元素(length/options)
        • select选项
      • 4.5. 访问表格子元素
    • 5. 修改HTML元素
    • 6. 增加HTML元素
      • 6.1. 创建和复制节点
        • 创建节点(createElement)
        • 复制节点(cloneNode)
      • 6.2. 添加节点(添加子节点,都需要先获取父节点)
      • 6.3. 为列表框、下拉菜单添加选项
    • 7. 删除HTML元素
      • 7.1. 删除节点(先获取父节点,删除某个子节点)
      • 7.2. 删除列表框、下拉菜单选项
      • 7.3. 删除表格的行或单元格


基于对象:js内置的对象或者类
面向对象:自己创建对象或者类


三、面向对象的JavaScript

1. 对象应用

1.1. 声明和实例化

对象的创建方式是用关键字 new 后面跟上实例化的类的名字:

var oObject = new Object();
var oStringObject = new String();

下面这种方式也可以,不推荐。
var oObject = new Object;
var oStringObject = new String;

1.2. 早绑定和晚绑定

// 晚绑定(后期绑定):动态语言的特点。属性和方法在运行过程中添加的。
// java:前期绑定,静态语言的特点,先编译后执行,先定义好
var obj = new Object();
obj.name = "张三";
obj.age = 24;
obj.study = function(){
	alert("张三在学习");
}
	obj.study();

2. 对象作用域

2.1. 公用、私有和受保护作用域

ECMAScript 中只存在一种作用域 - 公用作用域。
由于缺少私有作用域,开发者确定了一个规约,说明哪些属性和方法应该被看做私有的。这种规约规定在属性前后加下划线:

obj._color_ = "blue";

2.2. 静态变量/方法

ECMAScript 没有静态作用域
不过,它可以给构造函数提供属性和方法。

function sayHello() {  // 相当于 var sayHello = f()  函数也是变量,可以加属性sayHello.alternate
  alert("hello");
}
sayHello.alternate = function() {
  alert("hi");
}

sayHello();		//输出 "hello"
sayHello.alternate();	//输出 "hi"

2.3. 关键字 this

// this
var oCar = new Object; //创建一辆汽车
oCar.color = "red"; // 汽车颜色
oCar.showColor = function() { // 给汽车赋一个方法
	alert(this.color); // this代表shouColor方法属于哪一个对象 oCar.showColor当然是oCar
	// 为什么不直接使用oCar.color? 解决两个对象同时调用此方法
};
oCar.showColor(); //输出 "red"


// 原因:
var oCar1 = new Object;
oCar1.color = "red";
oCar1.showColor = showColor;  //不加括号是变量,加括号是调用。
// 写成 var shouColor = function(){alert(this.color); } 就好理解了。此写法更流行

var oCar2 = new Object;
oCar2.color = "blue";
oCar2.showColor = showColor;

function showColor() {
	alert(this.color); // oCar1和oCar2都调用此方法。oCar1调用时this就是oCar1,oCar2同理
};		
			
oCar1.showColor(); //输出 "red"
oCar2.showColor(); //输出 "blue"

3. 定义类和对象

3.1 工厂方式

原始方式

// 原始方式。在另外页面创建汽车,也需要重写一遍
			var oCar = new Object; // 创建空对象
			oCar.color = "blue"; // 动态添加属性和方法
			oCar.doors = 4;
			oCar.mpg = 25;
			oCar.showColor = function() {
				alert(this.color);
			};

工厂方式

// 工厂方式:把创建的对象封装在一个函数中,给个返回值
			// 放在外部js文件中,可多次调用。属性值可以通过传参更改
			function createCar() {
				var oTempCar = new Object;
				oTempCar.color = "blue";
				oTempCar.doors = 4;
				oTempCar.mpg = 25;
				oTempCar.showColor = function() {
					alert(this.color);
				};
				return oTempCar;
			}
			var oCar1 = createCar();

为函数传递参数

// 传参
			function createCar(sColor, iDoors, iMpg) {
				var oTempCar = new Object;
				oTempCar.color = sColor;
				oTempCar.doors = iDoors;
				oTempCar.mpg = iMpg;
				oTempCar.showColor = function() { // 每调用一次创建一次
					alert(this.color);
				};
				return oTempCar;
			}

			var oCar2 = createCar("blue", 3, 25);
			oCar2.showColor(); //输出 "blue"

在工厂函数外定义对象的方法

前面的例子中,每次调用函数 createCar(),都要创建新函数 showColor(),意味着每个对象都有自己的 showColor() 版本。而事实上,每个对象都共享同一个函数。

			function showColor() { // 所有对象共享同一个方法
				alert(this.color);
			}

			function createCar(sColor, iDoors, iMpg) {
				var oTempCar = new Object;
				oTempCar.color = sColor;
				oTempCar.doors = iDoors;
				oTempCar.mpg = iMpg;
				oTempCar.showColor = showColor;
				return oTempCar;
			}

			var oCar1 = createCar("red", 4, 23);
			var oCar2 = createCar("blue", 3, 25);
			oCar1.showColor(); //输出 "red"
			oCar2.showColor(); //输出 "blue"

3.2. 构造函数方式

// 构造函数方式。java原理一样
			function Car(sColor, iDoors, iMpg) {
				this.color = sColor;
				this.doors = iDoors;
				this.mpg = iMpg;
				this.showColor = function() {
					alert(this.color);
				};
			}
			
			// 调用:new Car
			var oCar1 = new Car("红色", 4, 23);  // this就是new出来的函数的对象。
			// 不用new就是函数,没有返回值,会报错
			oCar1.showColor();  // 调用对象的方法
			var oCar2 = new Car("blue", 3, 25);

3.3. 原型方式(prototype 属性)

该方式利用了对象的 prototype 属性,可以把它看成创建新对象所依赖的原型。
首先用空构造函数来设置类名。然后所有的属性和方法都被直接赋予 prototype 属性。
调用 new Car() 时,原型的所有属性都被立即赋予要创建的对象,意味着所有 Car 实例存放的都是指向 showColor() 函数的指针。

// 原型方法。 
			function Car() {}  // car所有的对象自然的具备下面这些属性和方法

			Car.prototype.color = "blue";
			Car.prototype.doors = 4;
			Car.prototype.mpg = 25;
			Car.prototype.showColor = function() {   // 只会创建一个,在本体上。
				alert(this.color);
			};

			var oCar1 = new Car();   // 所有 Car 实例存放的都是指向 showColor() 函数的指针
			oCar1.showColor();
			var oCar2 = new Car();

问题:

  • 这个构造函数没有参数。使用原型方式,不能通过给构造函数传递参数来初始化属性的值,因为 Car1 和 Car2 的 color 属性都等于 “blue”,doors 属性都等于 4,mpg 属性都等于 25。这意味着必须在对象创建后才能改变属性的默认值。
  • 一个对象被改动,都被改动
			function Car() {}

			Car.prototype.color = "blue";
			Car.prototype.doors = 4;
			Car.prototype.mpg = 25;
			Car.prototype.drivers = new Array("Mike", "John");
			Car.prototype.showColor = function() {
				alert(this.color);
			};

			var oCar1 = new Car();
			var oCar2 = new Car();

			oCar1.drivers.push("Bill");  // 串了

			alert(oCar1.drivers); //输出 "Mike,John,Bill"
			alert(oCar2.drivers); //输出 "Mike,John,Bill"

3.4. 混合的构造函数/原型方式

混合方式: 属性–构造函数;方法–原型方式

		<script>
			// 混合方式: 属性--构造函数;方法--原型方式
			function Car(sColor, iDoors, iMpg) {
				this.color = sColor;
				this.doors = iDoors;
				this.mpg = iMpg;
				this.drivers = new Array("Mike", "John");
			};
			Car.prototype.showColor = function() {
				alert(this.color);
			}
			
			
			var oCar1 = new Car();
			var oCar2 = new Car();
			oCar1.drivers.push("Bill");
			alert(oCar1.drivers); //输出 "Mike,John,Bill"
			alert(oCar2.drivers); //输出 "Mike,John"
		
		</script>

3.5. 动态原型方法

		<script>
			function Car(sColor, iDoors, iMpg) {
				this.color = sColor;
				this.doors = iDoors;
				this.mpg = iMpg;
				this.drivers = new Array("Mike", "John");

				if(typeof Car._initialized == "undefined") {
					Car.prototype.showColor = function() {
						alert(this.color);
					}
					Car._initialized = true;
				}
			}
		</script>

3.6. 建议方式

目前使用最广泛的是混合的构造函数/原型方式。此外,动态原始方法也很流行,在功能上与构造函数/原型方式等价。可以采用这两种方式中的任何一种。
不过不要单独使用经典的构造函数或原型方式,因为这样会给代码引入问题。

四、DOM(文档对象模型)

1. DOM模型概述

DOM(Document Object Model)文档对象模型。
HTML就是一棵树,要修改的话,需要先把树加载到内存中,在内存中的树就是DOM。
DOM采用直观、一致的方式对结构化文档进行模型化处理,形成一颗结构化的文档树,从而提供访问、修改该文档的简易编程接口。

2. DOM树和节点

所有 HTML 元素(节点)均可被修改,也可以创建或删除节点。

3. Document 对象

每个载入浏览器的 HTML 文档都会成为 Document 对象。Document 对象使我们可以从脚本中对 HTML 页面中的所有元素进行访问。Document对象无需我们自己创建,Document对象是由浏览器帮我们生成。通过window.document便可以直接调用(window.可以省略)

Document对象查找:  只有id是单数
    getElementById():通过节点的id属性,查找对应节点。 // 返回一个元素
    getElementsByName():通过节点的name属性,查找对应节点。  // 返回数组
    getElementsByTagName():通过节点名称,查找对应节点。
    getElementsByClassName():通过节点class属性,查找对应节点。
使用Document对象的方法创建节点:
    createElement(tagName):创建元素节点。
    createTextNode(data):创建文本节点。(例如动态的给span标签添加内容)

4. 访问HTML元素

4.1. 根据节点访问HTML元素(getElement(s)By)

getElementById()和getElementsByName()方法属于document的方法。
getElementsByTagName()和getElementsByClassName()方法属于所有元素节点的方法。也就是说可以

// 只获取表格里的input标签:方法一
var inp = document.getElementById("tab").getElementsByTagName("input");  // table下的input		
for(var i = 0; i < inp.length; i++) {
	console.info(inp[i]); // 浏览器控制台输出
}

4.2. 根据CSS选择器访问HTML元素(querySelector(All)())

  • querySelector:返回HTML文档中第一个符合选择器参数的HTML元素
// 只获取表格里的input标签:方法二
var inp = document.querySelector("#tab").getElementsByTagName("input");	
for(var i = 0; i < inp.length; i++) {
	console.info(inp[i]); // 浏览器控制台输出
}
  • querySelectorAll:返回所有符合CSS选择器的HTML元素
// 方法三:
var inp = document.querySelector("#tab").querySelectorAll("input");
for(var i = 0; i < inp.length; i++) {
	console.info(inp[i]); // 浏览器控制台输出
}

4.3. 利用节点关系访问HTML元素(有坑)

Node parentNode: 返回当前节点的父节点。只读属性。(√)

  • 这6个是大坑!!!!!不推荐用。
    下面的六个都把换行当成text元素,所以前一个兄弟可能不是你想象的那个
Node previousSibling:返回当前节点的前一个兄弟节点。 只读属性。
Node nextSibling: 返回当前节点的下一个兄弟节点。只读属性。
Node[] childNodes: 返回当前节点的所有子节点。 只读属性。
Node[] getElementsByTagName(tagName): 返回当前节点的具有指定标签名的所有子节点。
Node firstChild: 返回当前节点的第一个子节点。 只读属性。
Node lastChild: 返回当前节点的最后一个子节点。 只读属性。
  • 这六个不考虑空格,只考虑有效元素
// 不考虑空格,只考虑有效元素
Element parentElement: 返回当前节点的父元素。只读属性。
Element previousElementSibling:返回当前节点的前一个兄弟元素。 只读属性。
Element nextElementSibling: 返回当前节点的下一个兄弟元素。只读属性。
Element[] children: 返回当前节点的所有子元素。 只读属性。
Element firstElementChild: 返回当前节点的第一个子元素。 只读属性。
Element lastElementChild: 返回当前节点的最后一个子元素。 只读属性。

  • 坑举例:
		<table>
			<tr>
				<td>按钮</td>
				<td id="tdButtons">
					<input type="button" value="注册" onclick="submitForm()" />
					<input type="reset" value="取消" />
					<input type="button" value="增加元素" id="addElement" />
				</td>
			</tr>
		</table>
// 坑:previousSibling 并不能给取消按钮加背景色?为什么呢
document.getElementById("addElement").previousSibling.style.backgroundColor="red"

查看它的兄弟节点是什么:把换行当成了类型为text的元素

// 查看一下兄弟节点是什么
			var cs = document.getElementById("tdButtons").childNodes;
			for (var i=0; i<cs.length; i++){
				console.info(cs[i])
			}

JavaScript笔记(三)_第1张图片
解决方法:
使用previousElementSibling替换previousSibling。
只获得所有子节点使用children。

document.getElementById("addElement").previousElementSibling.style.backgroundColor = "red";
var cs = document.getElementById("tdButtons").children;
for (var i=0; i<cs.length; i++){
	console.info(cs[i])
}

4.4. 访问列表框、下拉菜单选项

select元素(length/options)

form:返回select元素所在的表单对象,只读属性。
length:返回select元素的选项个数。只读属性。
options:返回select元素里所有选项组成的数组,只读属性。

selectedIndex:返回下拉列表中选中选项的索引,如果有多个选项被选中,则只返回第一个被选中选项的索引,读写属性。
type:返回下拉列表的类型,即是否允许多选。如果允许多选,则返回select-multiple。如果不支持多选,则返回select-one。
multiple:将select元素变为多选。

		<table>
			<tr>
				<td>学历</td>
				<td >
					<select id="degree">
						<!--value为数据库的值,方便快速查询 option中间的文本为了显示给用户-->
						<option value="1">博士</option>
						<option value="2">硕士</option>
						<option value="3">本科生</option>
						<option value="4">专科生</option>
					</select>
				</td>
			</tr>
		</table>
		var sel = document.getElementById("degree");
		console.info(sel.length);
		console.info(sel.options);
		for (var i=0;i<sel.length; i++){
			// sel.options[i]是对象;
			// sel.options[i].text是option标签之前的文本;
			// sel.options[i].value 是option中value的值
			console.info(sel.options[i].text)   // 
		}
		
		// 查看类型
		console.info(sel.type);  // select默认就是单选:select-one
		sel.multiple = true;    // 更改为多选。 尽量在html中写 的指定选项赋值。
			}

7. 删除HTML元素

7.1. 删除节点(先获取父节点,删除某个子节点)

删除节点需要借助其父节点,Node对象提供了如下方法来删除子节点。
removeChild(oldNode): 删除oldNode子节点。
从父节点中删除该子节点后,该子节点就是在文档中消失。

7.2. 删除列表框、下拉菜单选项

删除列表框、下拉菜单的选项有两种方法:

  1. 利用HTMLOptionElement对象的remove()方法删除选项。
  2. 直接将指定选项赋值为null。

如果想要删除某个列表框、下拉菜单的全部选项,没有必要采用循环的方式逐一删除每个选项,将列表框或下拉菜单的innerHTML属性赋值为null,即可一次性删除该列表框的全部选项。

7.3. 删除表格的行或单元格

function delEle() {
	var sel = document.getElementById("degree");
	// 一、删除节点--removeChild(oldNode): 删除oldNode子节点。
	sel.removeChild(sel.options[2]);
				
	// 二、删除列表框、下拉菜单选项 两种方法:
	// 1. 利用HTMLOptionElement对象的remove()方法删除选项。
	// 2. 直接将指定选项赋值为null。
	// 如果想要删除某个列表框、下拉菜单的全部选项,直接将列表框或下拉菜单
	// 的innerHTML属性赋值为null,即可一次性删除该列表框的全部选项。
				
	// 三、删除表格的行或单元格:
	// 删除表格指定的表格行使用HTMLTableElement 类对象的下列方法:
	// deleteRow(long index): 删除表格中index索引处的行。
	// 删除表格行指定的单元格,使用HTMLRowElement对象的如下方法:
	// deleteCell(long index): 删除某行 index 索引处的单元格。		
}

你可能感兴趣的:(前端)