JavaScript高级知识(二)

JS面向对象的特点

  • 在JS中,有对象,没有类(但有构造函数),因此,有人说JS是“基于对象”而非面向对象,其实JS的面向对象非常灵活,比起静态语言,能达到意想不到的效果。

  • JS的对象不依赖类而存在,可以直接生成。

1
var chicken = {leg:2,sing:function(){alert("在下是鸟!");}};    //json格式
  • JS对象的属性可以任意添加和删除(方法和属性不必严格区分)
1
2
3
var chicken.arm = 2;    //添加属性
delete chicken.sing; //删除方法
对象的三大特点:封装,继承,多态

私有属性与封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//封装
function Dog(){
this.leg = 4;
this.bark = function(){
alert("汪汪");
}
}
var huzi = new Dog();
huzi.bark(); //输出 汪汪
alert(huzi.leg); //输出 4
/*
此处,我们并没有完成面向对象的“封装”,所谓封装:就是要封闭一部分,外界无法访问,
开放一部分,通过开放部分简介访问私有部分。
*/

//通过闭包完成私有属性的封装
function Dog(name,feature){
var private = feature; //外界无法直接访问,只能通过对象属性调用
this.leg = 4;
this.eat = function(){
return feature;
}
this.favorite_eat = function(){
feature = "骨头";
}
}
var dog = new Dog("huzi","吃屎");
alert(dog.name + "喜欢" + dog.eat());
alert(dog.name + "喜欢" + dog.favorite_eat());

详解原型链继承

JS没有类的概念,因此,JS的继承,不是通过类的继承来实现的,而是通过“原型”的概念来完成的。

QQ20171226-223004.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//创建cat的构造函数
function Cat(){
this.climb = function(){
alert("我会爬树");
}
}
//创建tiger的构造函数
function Tiger(){
this.bark = function(){
alert("我是百兽之王");
}
}

//下面让虎继承猫的属性:爬树
Tiger.prototype = new Cat();
var hnhu = new Tiger();
hnhu.climb();
hnhu.valueOf();

老虎是如何爬树和valueOf的呢?

  • 老虎先在自身对象上寻找,没有爬树方法,valueOf()方法,去找原型,原型cat对象上由此方法,得以调用climb()方法,但valueOf()仍没有找到,因此,继续沿着原型查找,找到cat空对象,仍没有valueOf()放法,再找,找到Objecte对象,有valueOf()方法,调用成功。

  • 对象–>原型–>原型的原型–>Object对象–>Null,这样的一条链称为原型链

  • 对象的属性和方法,就是沿着原型链查找和调用的,也就是JS中的原型继承

1
2
3
4
5
6
7
8
9
10
//给所有对象添加一个方法
Object.prototype.sing = function(){
alert("我会唱歌");
}

function Pig(){
this.eat = "10KG";
}
var zhu = new Pig();
zhu.sing();

原型冒充及复制继承

JS的语法非常灵活,不仅可以用原型继承,还有其他办法,如:原型冒充或复制继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//原型冒充
function Cat(leg,tail){
this.leg = leg;
this.tail = tail;
this.climb = function(){
alert("我会爬树");
}
}

function Tiger(leg,tail,color){
//把要继承的类的语句,拿来执行一遍
this.parent = Cat; //把父类构造函数引入到一个parent属性上
this.parent.apply(this,arguments); //获取Cat参数
this.color = color;
delete this.parent;
}

var tiger = new Tiger();
tiger.climb();

  • 用Tiger造对象时,用Tiger语句影响一个空对象{ },在此过程中,Tiger影响空对象前,先由Cat函数实施影响,因此,最终得到的对象,是由Cat和Tiger两者共同作用过得对象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//复制继承
function Cat(leg,tail){
this.leg = leg;
this.tail = tail;
this.climb = function(){
alert("我会爬树");
}
}

function Tiger(leg,tail,color){
this.color = color;
this.extend = function(parent){
for(var key in parent){
this[key] = parent[key];
}
}
}

var tiger = new Tiger("yellow");
tiger.extend(new Cat(4,1));
tiger.climb();

动态语言谈不上多态

JS谈不上多态,因为即JS就没有限制参数类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
function Dog(){
this.leg = 4;
this.bark = null;
}
function Hashiqi(){
this.bark = function(){
alert("wuwu");
}
}

function Jingba(){
this.bark = function(){
alert("wuwu");
}
}

Hashiqi.prototype = Jingba.prototype = new Dog();
var h = new Hashiqi();
var j = new Jingba();

function test(dog){
dog.bark();
}

test(h);
test(j);

  • 传入不同的狗,叫声也不同

JS面向对象之静态方法

  • 构造函数通过new来制造对象

  • 函数本身也是对象

1
2
3
4
5
6
7
8
9
10
11
12
13
function Machine(){
this.on = function (){
alert("我可以制作豆浆!");
}
}

Machine.heat = function(){
alert("加热!");
}

var m = new Machine(); //m没有heat方法,就像豆浆机的加热功能不会跑到豆浆里
m.on();
Machine.heat();
  • heat方法属于函数本身的,和返回的对象没有关系

  • on要调用,必须要new Machine();得到对象,且由返回对象才能调用

  • heat方法要调用,不需要new对象,直接用Machie来调用

绑定事件的2种常用方式

image

  • 事件对象:事件发生的一瞬间,关于事件的各种信息,如时间,如发生时鼠标在屏幕上的坐标,事件类型等等,这些信息被打包成一个对象,便于我们获取
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/*
第一种事件绑定方式:把事件写在标签属性里,如:<a href="#" onclick="t()">百度</a>
这是DOM 0级标准(非常古老)
好处:大家都会,几乎所有的浏览器都支持
坏处:代码夹杂在HMTL中,不简洁,其次,这种事件写法,效率不高,再者不符合“行为,结构,样式”相分离
*/


//第二种事件绑定方式:用事件的属性来绑定函数
document.getElementById("test").onclick = function(){
alert("有人点我");
}
/*
好处1:完成了行为的分离
好处2:便于操作当事对象,因为function是作为对象的on***属性出现的,因此,函数里操作该对象,直接用this就能引用当事对象
好处3:方便读取事件对象
*/

addEventListener高级事件绑定

第三种事件绑定方式:W3c中的标准——addEventListener

  1. 绑定在哪个事件上?click,load,change,blur,focus等等

  2. 绑定什么函数?——自定义事件函数

  3. 什么方式监听执行事件函数?——捕捉,冒泡。

1
2
3
4
var test = document.getElementById('test');
test.addEventListener('click',function(){this.style.background = "gray";},false);
test.addEventListener('click',function(){alert("自学!");},false);
test.addEventListener('click',function(){alert("IT");},false);

细节:

  • 事件名一律不带on

  • 绑定事件函数的“this”,指绑定该事件的对象

  • 执行顺序是按绑定顺序来执行的。

捕捉模型与冒泡模型

image

1
2
3
4
5
6
7
8
9
10
function $(id){
return document.getElementById(id);
}
$("china").addEventListener('click',function(){alert("进入chian");},true);
$("bj").addEventListener('click',function(){alert("进入bj");},true);
$("hd").addEventListener('click',function(){alert("进入hd");},true);

$("china").addEventListener('click',function(ev){alert("离开chian");},false);
$("bj").addEventListener('click',function(){alert("离开bj");},false);
$("hd").addEventListener('click',function(){alert("离开hd");},false);

总结:

  • 第三个参数true/false代表捕捉/冒泡模型,如果不填,默认为false(不建议省略)

  • 系统会为事件函数自动传入事件对象,作为第一个参数传入:ev

事件停止传播与效果阻止

  • 事件(捕捉/冒泡)的过程中,如果想停止事件的传播,比如:被bj捕捉后,hd就不会再去捕捉了,事件到此停止,或hd冒泡后,事件结束,bj就不能冒泡。就需要使用事件对象的stopPropagation();函数
1
2
3
4
5
6
function $(id){
return document.getElementById(id);
}
$("china").addEventListener('click',function(){alert("进入chian");},true);
$("bj").addEventListener('click',function(ev){alert("进入bj";ev.stopPropagation(););},true);
$("hd").addEventListener('click',function(){alert("进入hd");},true);
  • 以表单为例,我想点击“onsubmit”时,检查是否填写完全,如果不完全,不让它提交,即取消事件本应有的效果,就需要使用事件对象的preventDefault();函数
1
2
3
4
5
$("form").addEventListener('submit',function(ev){
if($("age").value = ''){
ev.preventDefault();
}
},false);

解除绑定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<p>哈哈</p>
<input type="button" value="让你哭" onclick="addcry()">
<input type="button" value="让你笑" onclick="addla()">
<input type="button" value="你别哭" onclick="remcry()">
<input type="button" value="你别哭" onclick="remcry()">

<script>
function cry(){
alert("我想哭!");
}

function la(){
alert("我想笑!");
}

function addcry(){
document.getElementsByTagName('p')[0].addEventListener('click',cry,false);
}

function addla(){
document.getElementsByTagName('p')[0].addEventListener('click',la,false);
}

function remcry(){
document.getElementsByTagName('p')[0].removeEventListener('click',cry,false);
}

function remla(){
document.getElementsByTagName('p')[0].removeEventListener('click',la,false);
}
</script>

IE事件模型与标准事件模型详细对比

  • IE9及以上版本,已经支持W3c标准,在IE9以下版本中,是IE独有的事件模型,与W3c标准模型主要有以下几点不同:

  • 绑定事件的函数不一样,IE用attachEvent();,移除事件函数用detachEvent();

  • 事件必须要加on

  • 绑定的事件不是严格的顺序执行,是随机执行

  • W3c中,this指向对象本身,而IE模型中,this指向window

1
2
3
4
window.onload = function(){
document.getElementById('test1').attachEvent('onclick',function(){alert("捕捉");},true);
document.getElementById('test1').attachEvent('onclick',function(){alert("冒泡");},false);
}