修真院Web工程师零基础全能课
本节课内容
JavsScript对象
主讲人介绍
沁修,葡萄藤技术总监
项目经验丰富,擅长H5移动项目开发。
专注技术选型、底层开发、最佳代码实践规范总结与推广。
直播录屏版
传送门:https://v.qq.com/x/page/d0740...
课程大纲
1、对象属性
1.1 什么是对象
1.2 对象那个的特征
1.3 访问对象的属性
1.4 枚举对象的属性
2、创建对象
2.1 通过键值对创建
2.2 使用new关键字加构造函数
2.3 ES5的新规范object.create()
3、原型与继承
3.1 prototype
3.2 prototype的相关属性
3.3 prototype的子方法
4、事件event
4.1 事件类型
4.2 注册事件处理程序
4.2.1 设置对象属性
4.2.2 设置html标签属性
4.2.3 addEventListener
4.2.4 attachEvent()
4.3 事件的冒泡与捕获
4.3.1 冒泡
4.3.2 捕获
4.3.3 应用
1.对象属性
1.1 什么是对象?
对象是事物,在现实世界中,一个球,一张桌子,一辆汽车都是对象。
对象就是具有可描述特征的事物,我们可以用某种特定的方式去影响和操作它。
在JS中或者说在面向对象的编程语言中,对象是一系列属性和方法的组合。
一个属性包含属性名和属性值,这个值可以是任意类型的数据,也可以是个函数,这种情况下函数也被称为方法。
而在JS中,你遇到的所有东西几乎都是对象,那么我们来了解一下对象的细节。
1.2 对象的特征?
对象有属性,也就是对象的特征,可以通过点符号来访问一个对象的属性。
objectName.propertyName;
如果把对象比作现实中的一个球,那这个球就是一个对象,拥有属性,有颜色有大小。这些可以这样表示:
var ball = new Object();
ball.color = “red";
ball.size = 12;
对象还有方法,方法用于定义对象的行为方式。
比如这个球可能有滚动的方法可以计算能滚多远,有弹跳的方法可以计算跳多高。
可以这样表示:
ball.roll = function() {
return this.size * laps
}
1.3 访问对象的属性
前面讲过我们可以通过点符号来访问对象的属性,但如果是这样的情况,还能通过点符号访问对象的属性吗:
var ball = new Object();
ball.color = “red";
ball.size = 12;
var spec = “color”;
ball.spec ??
这里假设有一个变量spec,我们需要通过这个变量spec来访问对象属性。
但如果用点符号将会访问ball的spec属性然后返回一个undefined,说明这里直接将spec当做了一个属性而不是变量。
如果我们想要获得变量的值,来作为对象的属性那应该怎么做呢?
可以使用方括号标记访问,这个标记法在属性名称那里是动态判定的,这样就可以通过存储在变量中的字符串来访问属性了:
ball[spec]; // red
1.4 枚举对象的属性
另外我们可以通过for in循环语句来枚举对象的所有属性:
var ball = {color: “red”, size: 12, border: 2};
for (var prop in ball) {
console.log(“ball.” + prop + “=“ + obj[prop]);
}
// ball.color = red
// ball.size = 12
// ball.border = 2
// 如果希望继承的属性不显示,那么可以用hasOwnProperty函数来过滤一遍
var bar = {a: 1, b: 2, c: 3};
function Foo() {
this.color = 'red';
}
Foo.prototype = bar;
var obj = new Foo();
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
console.log(prop);
}
}
也可以通过Object.keys(obj)的方法来返回一个属性名集合的数组:
var obj = {a: “123”, b: “das”, c: “web”};
console.log(Object.keys(obj)); // [“a”,”b”,”c”];
var arr = [“a”, “b”, “c”];
console.log(Object.keys(arr)); // [“0”,”1”,”2”];
2.创建对象
在JS中创建对象的方法通常有三种:
2.1 第一种
对象字面量的方式,使用花括号,直接通过键值对来创建:
var ball = {};
JS每次遇到这个表达式的时候都会创建一遍对象。
2.2 第二种
使用new关键字加构造函数:
var ball = new Object();
比如我们想为球创建一个类型,并且将这类对象称为Ball,拥有一些属性,那么可以通过两步来创建对象:
1.创建一个构造函数Car来定义这个对象类型,首字母通常为大写,这是种习惯用法。
2.通过new创建对象实例:
function Ball(color, size, border) {
this.color = color;
this.size = size;
this.border = border;
}
var myBall = new Ball(“white”, 9, 1);
通过这样的方法可以创建许多实例,每个实例之间互不影响。
2.3 第三种
ES5的新规范,Object.create(),它允许你为创建的对象选择其对象原型,不用定义一个构造函数
var Ball = {
color: “red”,
size: 12
}
var ball1 = Object.create(Ball);
这每一种创建方式继承的原型对象都不同:
第一种的原型是Object.prototype
第二种的原型是构造函数的prototype属性
第三种的原型是传入的第一个参数,如果参数为空则原型为Object.prototype。
3.原型与继承
3.1 prototype
我们先来看看这段代码:
var foo = {};
console.log(foo.toString()); //[Object Object]
这段代码意味着什么?我们从一个空的对象中取出了一个属性?
事实上并不是的,我们将要了解一些JS对象的内部工作细节了:
每个对象除了拥有自己的属性外,几乎都包含了一个原型prototype,继承的属性可以通过构造函数的prototype对象找到。
当我们访问一个对象所不包含的属性时,它会从对象的原型中去搜索,原型的原型中去搜索,一直循着原型链往上直到搜索到或者到顶部为止。
说到prototype,其实每个构造函数都有prototype这个属性,它指向另外个对象,而这个对象上所有的属性和方法都会被构造函数的实例所继承。
这就意味着我们可以把不变的属性和方法,定义到prototype上去:
function Car(color) {
this.color = color;
}
var c1 = new Car(‘white’);
var c2 = new Car(‘red’);
Car.prototype.callColor = function() {
alert(this.color);
}
c1.callColor();
c2.callColor();
这里所生成的实例c1和c2,他们的callColor方法其实都是同一个内存地址,都是指向的同一个prototype对象,这样提高了运行效率:
c1.callColor == c2.callColor; // true
3.2 prototype的相关属性
通常来讲,prototype用于设置和返回构造函数的原型对象,方便实例去继承。
那和它相关的属性还有些什么呢?
还有2个,它们几个纠结于构造函数和实例和原型之间,大家尽量弄清楚。
__proto__: 设置或返回实例的原型对象
// 1.对象直接量
var p = {}; // 等于new Object()
console.log(p.__proto__ == Object.prototype); // => true
// 2.自定义对象多层继承
function People(name) {
this.name = name;
}
function Student(age) {
this.age = age;
}
Student.prototype = new People(); // 设置Student的原型为People对象
var s = new Student(22);
console.log(s.__proto__); // => People 对象
console.log(Student.prototype); // => People 对象
console.log(s.__proto__ == Student.prototype); // => true
constructor: 表示实例对象的构造函数
// 1.内置对象
var str = 'abc';
console.log(str.constructor); // => function String 构造函数
var o = {};
console.log(o.constructor); // => function Object 构造函数
// 2.自定义对象多层继承 :constructor返回最先调用的构造函数
function People(name) {
this.name = name; // s对象初始化时,先调用People构造函数,再调用Student构造函数
console.log('People调用');
}
function Student(age) {
this.age = age;
console.log('Student调用');
}
Student.prototype = new People(); // 设置Student的原型为People对象
var s = new Student(22);
console.log(s.constructor); // => function People 构造函数
他们的关系可以总结为:
var o = {};
console.log(o.__proto__ === Object.prototype); // true :实例化对象的__proto__等于类的prototype
console.log(o.constructor === Object); // true :实例化对象的constructor等于类
console.log(o.constructor.prototype === Object.prototype); // true :o.constructor.prototype 可表示类的原型。
3.3 prototype的子方法
另外值得说一下的是prototype属性,它还有两个辅助方法,帮助我们去使用它:
isPrototypeOf():
这个方法用来判断构造函数的原型是否是某个实例的原型:
Ball.prototype.isPrototypeOf(b1);
Ball.prototype.isPrototypeOf(b2);
hasOwnProperty():
这个方法用来判断实例的一个属性是不是本地属性,还是说是继承自prototype的属性:
b1.hasOwnProperty(“color”);
b1.hasOwnProperty(“jump”);
4.事件event
4.1 事件类型
事件类型就是一个用来说明发生什么类型事件的字符串
例如mouseover表示用户移动鼠标,keydown表示键盘上某个按键被按下,onload表示文档加载完毕。
在web应用中,处理表单、窗口、鼠标和键盘是最最常用的类型了。
表单类型,比如当用户提交表单和重置表单时,form元素就会分别触发submit和reset事件;
当用户改变单选框复选框这些元素的状态时,通常会触发change事件。
窗口类型,则是事件和文档内容无关,会与浏览器窗口本身相关。
比如load事件就是最重要的一个,当文档和外部资源完全加载并显示出来后就会触发它;
还有当用户调整窗口大小或滚动的时候会触发resize事件和scroll事件。
鼠标类型,就是用户在文档上移动或点击时会触发鼠标事件。
用户移动鼠标时,就会触发mousemove事件,不过这个事件发生非常频繁,千万不要用来计算密集型的任务;
用户按下或释放鼠标按键的时候,会触发mousedown和mouseup事件。
键盘类型,当键盘聚焦到浏览器时,用户每次按下或释放按键时都会产生事件,传递给处理程序的时间对象有keyCode字段,指定了按下或释放的是哪个键。
4.2 注册事件处理程序
在指明了事件类型后,当特定目标上发生这个类型的事件时,就会调用对应的处理程序,也就是我们经常所说的触发了事件。
这就需要我们注册事件的处理程序,通常有这样几种方式:
4.2.1 设置对象属性
这种写法都是由on后面跟着事件名组成:
onclick、onchange、onload等等,并且是全小写:
// 设置window对象的onload属性为一个函数
window.onload = function() {
}
这种方法适合于所有浏览器,但缺点是,一个事件目标的一个事件类型下最多就只能有一个处理程序。
更好的办法后面会讲到。
4.2.2 设置html标签属性
这种写法是写在html标签的属性里,属性值是一个代码字符串:
<button οnclick=“alert(‘a string!’);”>click here</button>
浏览器会把代码字符串转换为代码来执行。
但这样做混合js和html,不符合html内容和js行为分离的标准,因此也不是一个好的写法。
4.2.3 addEventListener()
在除了IE9之前的所有浏览器都支持一个方法,addEventListener(),使用这个方法可以为事件目标注册事件处理程序,它可以接收3个参数。
第一个是事件类型,这是个字符串但不包括前缀on;
第二个参数是指定事件发生时应该调用的函数;
最后一个参数是布尔值,通常情况下会传递一个false,但如果传成了true那么函数会注册为捕获事件处理程序。
<button id=“btn”>click me</button>
<script>
var btn = document.getElementById(“btn”);
btn.addEventListener(“click”, function() {
alert(“clicked!”);
}, false);
</script>
4.2.4 attachEvent()
因为IE9以前的IE不支持addEventListener,所以有个类似的方法attachEvent(),其原理和addEventListener差不多,但也有点不同:
1.因为它不支持事件捕获,因此只有两个参数:事件类型和处理函数。
2.事件类型传递的字符串需要有前缀on
结合这两种方法,经常看到的事件处理程序注册的代码是这样的:
var btn = document.getElementById(“btn”);
var handler = function() {alert(“hello world”);};
if (btn.addEventListener) {
btn.addEventListener(“click”, handler, false);
} else if (btn.attachEvent) {
btn.attachEvent(“onclick”, handler);
}
这样就是在支持addEventListener的浏览器中就调用它,否则就用attachEvent。
4.3 事件的冒泡与捕获
有这么一个思想,我们都知道一个html页面就是一颗DOM树,那么树上任意一个节点发生的事件是否都可以通过攀爬这课树得知呢?
答案是可以的,这就是事件流。
事件流有冒泡和捕获两种。
4.3.1 冒泡
首先来看一下冒泡:
当click事件在div上发生后,click事件沿着DOM树向上传播,直到document对象上。
这是一种自下而上的顺序,所有的现代浏览器都支持事件冒泡。
4.3.2 捕获
还有一种思路则是捕获,自上而下,其用意在于事件到达目标之前就捕获它:
在捕获过程中,document对象首先接收到click事件,然后沿着DOM树一路向下,一直传播到事件的实际目标div元素。
但因为老版本浏览器不支持,因此很少有人使用事件捕获,所以建议大家放心使用事件冒泡,特殊需要时再考虑事件捕获。
4.3.3 应用
在了解了基本概念之后,就可以开始考虑实际编码了:
<body>
<div id=“content">
content
<p id=“child">child</p>
</div>
<script>
var content = document.getElementById(“content”);
var child = document.getElementById(“child”);
content.addEventListener(“click”, function() {
alert(“content”);
}, false);
child.addEventListener(“click”, function() {
alert(“child”);
}, false);
</script>
</body>
可以看到这是文档结构非常简单:
body>#content>#child。
分别在content和child上面绑定了一个点击事件,由于addEventListener第三个参数是false,所以是冒泡阶段处理绑定事件。
这个时候点击文本页面有两种行为出现:
1.点击content文本,弹出content。
这个时候就只处罚了content上的点击事件,从content文本节点查找,当前节点没有点击事件;
然后往上找,找到父级#content有绑定事件执行;
再往上找直到document都没有绑定的点击事件,至此冒泡过程结束。
2.点击child文本,先弹出child,再弹出content
理解了上面的代码之后,我们就可以有事件代理,假设我们有成千上万个.child标签需要绑定点击事件弹出内容,就没法去挨个绑定了,而且性能也会降低。
这个时候就可以这样做:
var content = document.getElementById("content");
content.addEventListener("click", function(e) {
// 把事件对象当做参数传递过来,但在IE浏览器中,这个e变量是空的,它会在全局的window上,因此这样写了之后就可以两种方式都支持,保证了浏览器的兼容性
var e = e || window.event;
if (e.target.className == "child") {
alert(e.target.innerHTML);
}
}, false);
由于事件冒泡机制的存在,点击了.child之后会冒泡到content上,这个时候就会触发绑定在content上的点击事件,再利用target找到事件实际发生的元素,就可以达到预期效果了
以上就是上节课的内容解析啦
想进一步深入的同学欢迎加入我们的IT交流群828691304共同交流学习!
下节预告
jQuery入门与应用