基础概念和语法
基本类型、null、undefined、NaN
JavaScript定义了一小批基本类型(primitive type),它们包括字符串类型(string,单引号或双引号包起来
)、数值类型(number,整数和小数都用这个类型
)和布尔类型(boolean,值仅有true和false
)。
JavaScript使用两个特殊的值来表示不存在有意义的值——null和undefined。前者表明无值(non-value),是有意为之的;而后者表明变量尚未赋值。
JavaScript中Boolean类型的原始值是由关键字true和false描述的。下面的规则说明了什么是false,什么是true:
false、0、空字符串("")、NaN、null和undefined都被视为false,其他的都被视为true
JavaScript另一个特殊之处在于它有一个叫作NaN(Not a Number的缩写)的特殊值。一般来说,这个值会出现在类型(String、Boolean等)转换失败的时候。如果NaN出现在数学运算中的任何一部分,结果都会变成NaN。作为一个通用规则,不要在任何表达式中使用NaN。if(isNaN(NaN) === true){……}//如果不是个数值
注意:字符串连接运算符(
+
)比加法运算符(也是+
)优先级更高。比如:var a=5+'5' ,结果a的值是“55”,而不是10.
++和--
++和--操作符都是单目操作符,既可作为前缀,也可以作为后缀。它们所出现的位置很重要。当++作为前缀的时候,如++a,会先增加变量的值,然后再将该值从表达式返回,而不是像a++那样,先返回a的当前值,然后再增加。
逻辑运算符:
JS中的==和===
JS中编者非常推荐使用严格相等,非严格相等(==)可能会导致许多想不到的结果。
双等号==:
(1)如果两个值类型相同,再进行三个等号(===)的比较
(2)如果两个值类型不同,也有可能相等,需根据以下规则进行类型转换在比较:
1)如果一个是null,一个是undefined,那么相等
2)如果一个是字符串,一个是数值,把字符串转换成数值之后再进行比较
三等号===:
(1)如果类型不同,就一定不相等
(2)如果两个都是数值,并且是同一个值,那么相等;如果其中至少一个是NaN,那么不相等。(判断一个值是否是NaN,只能使用isNaN( ) 来判断)
(3)如果两个都是字符串,每个位置的字符都一样,那么相等,否则不相等。
(4)如果两个值都是true,或是false,那么相等
(5)如果两个值都引用同一个对象或是函数,那么相等,否则不相等
(6)如果两个值都是null,或是undefined,那么相等
严格模式
JavaScript的严格模式能够标出或消除JavaScript的一些静默错误(silent error)。严格模式能够为这些毛病抛出错误,而不是悄无声息地运行失败。严格模式也有助于将失误(mistake)变成实际的错误(error)。有两种强制执行严格模式的方法。如果希望将整个脚本置入严格模式,可以在你的JavaScript程序的第一行写上use strict语句。如果希望将严格模式限制在某个函数范围内,可以在函数的第一行写上这句指令:
function strictFn(){
// 该行使得其下的所有代码都处于严格模式
'use strict';
…
function nestedStrictFn() {
// 该函数内的代码也处于严格模式
…
}
}
异常处理:try catch
用于处理运行时异常,类似c#中的使用方式,以及,还可以使用抛出异常的 throw new Error(msg)
语句。如果catch 方法的参数用e表示,那么e.message 表示对错误信息描述的字符串。
function engageGear(gear){
if(gear==="R"){ console.log ("Reversing");}
if(gear==="D"){ console.log ("Driving");}
if(gear==="N"){ console.log ("Neutral/Parking");}
throw new Error("Invalid Gear State");
}
try
{
engageGear("R"); //Reversing
engageGear("P"); //Invalid Gear State
}
catch(e){
console.log(e.message);
}
函数
基本介绍
函数:JavaScript是门弱类型语言,所以定义函数的时候不必声明参数的数据类型。 调用带参数的函数时只要按声明函数时的参数顺序提供对应的值就好了。调用函数时提供的参数数目不必与函数定义中的参数数目相同,如果提供的参数值更少,那么所有未提供值的参数的值均为undefined
(且参数未定义默认值)。如果提供的参数值更多,那么多出的值会被忽略。其结果是,要想定义两个同名但参数数目不同的函数,然后让JavaScript根据调用函数时提供的参数值数目确定所调用的函数是不可能的。要是定义了两个同名的函数,那么第二个定义将会取代第一个。最后一点,如果函数想有返回值,那再函数的语句中直接用retun 就行了,retun后面是什么内容就返回什么内容,啥也没有就是跳出当前函数的效果。
JS函数中的this参数
当函数被调用时,除了在调用时明确给出的那些参数,还有一个叫作this的隐式参数也会被传入函数。它指向一个与此次函数调用相关联的对象,该对象被称为函数上下文(function context)。与Java类似,this指向定义了方法的类的实例。
1、作为函数调用
如果函数不是以方法、构造函数或通过apply()、call()调用,那么它只是作为一个函数被调用:
function add() {}
add();
var substract = function() {
};
substract();
当函数以这种模式调用时,this被绑定在全局对象上。
2、作为方法调用
方法是作为对象属性的函数。对于方法来说,this被绑定在方法被调用时所在的对象上:
var person = {
name: 'Albert Einstein',
age: 66,
greet: function () {
console.log(this.name);
}
};
person.greet();
让我们来对比一下在这两种调用模式中this的变化。
前两次调用都是函数调用。因此,this的值指向的是全局上下文(在这里是Window)。后面通过对象创建了testObjFunc()方法。如果调用该方法,在显示出this的值的时候就能够看出函数的上下文了。
3、作为构造函数调用
构造函数就是用来创建新的对象的函数,JavaScript中没有面向对象中的类,使用new前缀调用的函数被称为构造函数。构造函数的声明和其他函数一样,作为构造函数使用的函数也没有什么特别之处。但是构造函数的调用方式却大相径庭。
要以构造函数的方式调用函数,需要在函数调用前加上关键字new。这样的话,this就被绑定在新创建的对象上了。
var Person = function (name) {
this.name = name;
};
Person.prototype.greet = function () {
return this.name;
};
var albert = new Person('Albert Einstein');
console.log(albert.greet());
4、通过apply()和call()方法调用
我们之前说过JavaScript函数也是对象。和其他对象一样,函数也具备方法。要想使用apply()方法调用函数,需要传入两个参数:作为函数上下文的对象以及作为调用参数的数组。call()方法的用法也差不多,除了调用参数不能作为数组传入,而是要直接写成参数列表的形式传入。
数组
数组是值的有序集合。在JavaScript中,有三种创建数组的方法:
- var arr = [1,2,3];
- var arr = new Array(1,2,3);
- var arr = Array(1,2,3);
应该将var myArray = [100, "Adam", true];
这种方式作为首选,比较简洁。
注意点:
在大多数语言中,要求数组所有元素的类型必须相同。JavaScript允许数组中包含各种类型的值
你也可以给length属性赋值。如果赋予的值小于数组元素个数,数组会被截断;赋值0的话,会清空整个数组
给数组添加新元素,建议使用push方法
数组对象常用方法,参考 JavaScript Array 对象 | 菜鸟教程 (runoob.com)
forEach()方法提供了另一种迭代数组的方法:
var colors = ['red', 'green', 'blue'];
colors.forEach(function(color) {
console.log(color);
});
对象
JavaScript定义对象:
var myData = new Object();
myData.name = "Adam";
myData.weather = "sunny";
document.writeln("Hello " + myData.name + ". ");
document.writeln("Today is " + myData.weather + ".");
或者:
var myData = {
name: "Adam",
weather: "sunny",
myfuncgion1(){
return 100;
}
};
document.writeln("Hello " + myData.name + ". ");
document.writeln("Today is " + myData.weather + ".");
对象可以添加属性,也能添加函数(两种方式)。属于一个对象的函数称为其方法。
var myData = {
name: "Adam",
weather: "sunny",
myfuncgion1(){
return 100;
},
printMessages: function() {
document.writeln("Hello " + this.name + ". ");
document.writeln("Today is " + this.weather + ".");
//注意,在方法内部使用对象属性时要用到`this`关键字。函数作为方法使用时,其所属对象会以关键字`this`的形式作为一个参数被传给它。
}
};
myData.printMessages();
另外,对象的属性值可以是一个别的对象。
属性名可以是任意的字符串或空串。如果属性名是合法的JavaScript名称的话,可以省略其周围的引号。因此,对于first-name来说,引号是必需的;对于firstname来说,引号是可选的。
对象最显而易见的操作是读取或修改属性值。这些操作有两种不同的语法形式:
var myData = {
name: "Adam",
weather: "sunny",
};
myData.name = "Joe";
myData["weather"] = "raining";
myData.Age=18;//添加新属性。
document.writeln("Hello " + myData.name + ".");
//在这种类似从数组中取值的形式中,属性名作为一个字符串放在一对方括号之间。这种存取属性值的办法非常方便,这是因为此法可用变量表示属性名。
document.writeln("It is " + myData["weather"]);
注意:for/in 语句用于循环对象属性
var person = {fname:"John", lname:"Doe", age:25};
var text = "";
var x;
for (x in person) {
text += person[x] + " ";
}
对象的属性和方法可以用delete
关键字删除,如:delete myData.name;
若想为对象添加一个新的属性或修改某属性:直接如:myData.name = "大胜";
这样既可,当然也可以用类似数组的取值法,来添加属性或改变属性值。
可以用in
表达式判断对象是否具有某个属性,如:
var myData = {
name: "Adam",
weather: "sunny",
};
var hasName = "name" in myData; //true
var hasDate = "date" in myData; //false
注意:
window是js中的全局对象,我们创建的变量实际上是给window添加属性,所以这里可以用window点o对象。
Map,JS中的键值对集合
ECMAScript 6引入了map。map是一个简单的键值映射,可以依据元素的插入顺序进行迭代。
下面的代码片段展示了Map类型的一些方法及其用法:
var founders = new Map();
founders.set("facebook", "mark");
founders.set("google", "larry");
founders.size; // 2
founders.get("twitter"); // undefined
founders.has("yahoo"); // false
for (var [key, value] of founders) {
console.log(key + " founded by " + value);
}
// "facebook founded by mark"
// "google founded by larry"
Set,JS中的另一种数组(元素唯一)
ECMAScript 6引入了set。set是值的集合,可以依据其插入顺序进行迭代。set的一个重要特点就是其中的值只能出现一次。
下面的代码片段展示了set的一些基本操作:
var mySet = new Set();
mySet.add(1);
mySet.add("Howdy");
mySet.add("foo");
mySet.has(1); // true
mySet.delete("foo");
mySet.size; // 2
for (let item of mySet) console.log(item);
// 1
// "Howdy"
JS中的正则表达式
正则表达式是一种表示匹配字符串的模式的方法。表达式本身是由项(term)和操作符(operator)组成的,它们让我们得以定义这些模式。
在JavaScript中,创建正则表达式的方法有两种:通过正则表达式字面量和构造RegExp对象的实例。
如果想匹配字符串test,正则表达式的两种方式:
正则表达式字面量:var pattern = /test/;
正则表达式使用斜杠作为分隔符。我们也可以将正则表达式作为字符串传入RegExp(),从而创建出一个RegExp实例:var pattern = new RegExp("test");
这两种格式会生成相同的正则表达式,并保存在变量pattern中。除了正则表达式本身之外,还有三个标志可以配合使用。
-
i:可以使正则表达式忽略大小写,这样的话,/test/i不仅能匹配test,还能匹配Test、TEST、tEsT等。
-
g:相较于只匹配模式的第一次出现,g标志可以使正则表达式匹配模式的所有实例。
-
m:可以使正则表达式跨多行(例如textarea元素的值)进行匹配。
这些标志要放在正则表达式字面量的尾部(例如/test/ig),或是作为第二个字符串参数传给RegExp构造函数(new RegExp("test", "ig"))
定义好了正则表达式,就可以使用一些方法了,比如 test方法:
var patternGlobal = /orange/ig;
console.log(patternGlobal.test("Orange Juice")); // true
各种重复限定符如下:
匹配字符组
通过两种方式定义的正则表达式变量,有些常用方法:
exec()方法接受单个字符串作为参数,返回一个包含了所有匹配的数组
String对象的与正则相关的常用方法:
有一个match()方法,其功能与exec()方法类似。match()需要在String对象上调用,并将正则表达式作为参数传入。
replace(),它可以使用其他字符串来替换某个子串
var strToMatch = 'Blue is your favorite color ?';
var regExAt = /Blue/;
console.log(strToMatch.replace(regExAt, "Red"));
// 输出"Red is your favorite color ?"
面向对象的JavaScript
首先得先清楚JS中的对象的定义,属性的定义和增删改,以及对象的prototype属性,即一个对象的原型。
对象的默认属性:prototype
除了我们添加给对象的属性之外,所有的对象都还有一个叫作prototype的默认属性(object),prototype指向另一个叫作原型的对象。如果对象没有所请求的属性,JavaScript会到其原型(prototype)中去查找。Object.getPrototypeOf()方法会返回对象的原型。
当函数使用关键字new来调用时,它就会被作为构造函数使用。(注意,这很可能会导致函数里this的意义的变更)
这个原型对象也有自己的原型,以此类推,直到出现一个原型为null的对象。
当然,null没有原型,它作为原型链中的最后一环。
JS中没有面向对象语言中的类的概念,但可以使用new操作符,后面跟一个函数来实例化一个对象。
// 一个什么都不返回,也什么都不创建的函数
function Player() { }
// 向该函数的prototype属性中添加一个函数
Player.prototype.usesBat = function () {
return true;
}
// 以函数的形式调用player(),以证明什么都不会发生
var crazyBob = Player();
if (crazyBob === undefined) {
console.log("CrazyBob is not defined");
}
// 显示使用new,以构造函数的形式调用player()
// 1. 创建实例
// 2. 从函数的原型中得到方法usesBat()
var swingJay = new Player();
if (swingJay && swingJay.usesBat && swingJay.usesBat()) {
console.log("SwingJay exists and can use bat");
}
继承
在JavaScript中,可以通过将对象A的实例作为对象B的原型,来实现面向对象编程的继承关系(B继承A,B 是一个 A)。
function Person() {}
Person.prototype.cry = function() {
console.log("Crying");
}
function Child() {}
Child.prototype = new Person();
var aChild = new Child();
console.log(aChild instanceof Child); //true
console.log(aChild instanceof Person); //true
console.log(aChild instanceof Object); //true
JavaScript沿原型链(原型对象也有自己的原型)向上查找属性,直到遇见Object.prototype。
对象字段的设置和访问器:get set
类似于面向对象语言中的属性,JS有两种方式设置对属性的设置和访问
方式一
var person = {
firstname: "Albert",
lastname: "Einstein",
get fullname() {
return this.firstname +" "+this.lastname;
},
set fullname(_name){
var words = _name.toString().split(' ');
this.firstname = words[0];
this.lastname = words[1];
}
};
person.fullname = "Issac Newton";
console.log(person.firstname); //"Issac"
console.log(person.lastname); //"Newton"
console.log(person.fullname); //"Issac Newton"
方式二
var person = {
firstname: "Albert",
lastname: "Einstein",
};
Object.defineProperty(person, 'fullname', {
get: function() {
return this.firstname + ' ' + this.lastname;
},
set: function(name) {
var words = name.split(' ');
this.firstname = words[0];
this.lastname = words[1];
}
});
person.fullname = "Issac Newton";
console.log(person.firstname); //"Issac"
console.log(person.lastname); //"Newton"
console.log(person.fullname); //"Issac Newton"
JavaScript模式
模式是一种解决常见问题的模板即解决方案。为什么设计模式如此重要?
- 模式为常见问题提供了行之有效的解决方案:模式提供了解决特定问题的优化模板。坚实深厚的工程经验与经过验证的有效性为这些模式提供了保证。
- 模式旨在重用:它们具备通用性,适合于各种问题。
- 模式定义了词汇:模式是一种定义明确的结构,因而为解决方案提供了通用的词汇。这使得大型团队在沟通时能够非常清晰地表达出各自的意图。
接下来介绍针对JavaScript的模式,或者是这些经典模式的变形。即适用于JS,而且常用的设计模式
命名空间模式
命名空间能够减少程序创建的全局变量的数量,有助于避免命名冲突或过多的名称前缀。命名空间的思路是为应用程序或库创建一个全局对象,将所有的其他对象和函数全部添加到该对象中,而不是去污染全局作用域。
//不要这样:
function Car() {}
function BMW() {}
var engines = 1;
var features = {
seats: 6,
airbags:6
};
// 建议这样,单一全局对象
var CARFACTORY = CARFACTORY || {}; //这样写是因为该变量可能在其他地方已经有了
CARFACTORY.Car = function () {};
CARFACTORY.BMW = function () {};
CARFACTORY.engines = 1;
CARFACTORY.features = {
seats: 6,
airbags:6
};
全局命名空间对象名习惯上全部都是大写。这种模式为应用程序增加了命名空间,避免你的代码和其他代码及外部库发生命名冲突。
一些局限性:
尽管这看起来是个不错的方法,既可以限制全局变量,又能够为代码添加命名空间,但多少有点繁琐;你得在每个变量和函数前面加上命名空间前缀。需要输入的内容更多,代码也变得啰嗦。另外,单个全局实例意味着任何代码都能够修改该全局实例,进而影响到其他功能——这会造成让人非常伤脑筋的副作用。
模块模式
模块模式有助于维持代码清晰的独立性以及条理性。模块可以将较大的程序分隔成较小的部分,赋予其各自的命名空间。这一点非常重要,因为一旦将代码划分成模块,这些模块就能够在其他地方重新使用。精心设计的模块接口能够使代码易于重用和扩展。
模块模式利用闭包将变量和函数的访问限制在模块内部;如果定义在对象中的变量和函数被返回,那就是公共的了。模块可用于模拟类,强调的是对变量和函数的公共及私有访问。模块有助于减少全局作用域污染。有效地使用模块能够降低大型代码基础库之间的名称冲突。这种模式的典型用法如下:
var moduleName=function() {
// 私有状态
// 私有函数
return {
// 公共状态
// 公共变量
}
}
要实现以上模式有两个要求。
必须有一个外围函数,至少需要执行一次。
外围函数必须返回至少一个内部函数(inner function)。这需要创建一个涵盖了私有状态的闭包,否则无法访问私有状态。
请看下面的模块代码:
var superModule = (function (){
var secret = 'supersecretkey';
var passcode = 'nuke';
function getSecret() { console.log( secret ); }
function getPassCode() { console.log( passcode );}
return {
getSecret: getSecret,
getPassCode: getPassCode
};
})();
superModule.getSecret();
superModule.getPassCode();
工厂模式
该模式提供了一个用于创建对象的接口。根据传入工厂的类型,可以创建出特定类型的对象。
让我们用一个常见的例子来理解工厂模式的用法。假设我们有:
一个构造函数CarFactory()
CarFactory中一个叫作make()的静态方法,该方法知道如何创建car类型的对象
特定的car类型,例如CarFactory.SUV、CarFactory.Sedan等
我们希望像下面这样使用CarFactory:
var golf = CarFactory.make('Compact');
var vento = CarFactory.make('Sedan');
var touareg = CarFactory.make('SUV');
也没太get到
minxin
mixin模式能够显著减少代码中重复出现的功能,有助于功能重用。我们可以将能够共享的功能放到mixin中,以此降低共享行为的重复数量。
装饰器模式
装饰器模式的主要思想是使用一个空白对象(plain object)展开设计,该对象有一些基本的功能。随着设计的深入,可以使用现有的装饰器来增强该空白对象。
观察者模式
在GOF的《设计模式》一书中,是这样定义观察者模式的:
对目标状态感兴趣的一个或多个观察者,通过将自身与该目标关联在一起的形式进行注册。当目标出现观察者可能感兴趣的变化时,发出提醒消息,进而调用每个观察者的更新方法。如果观察者对目标状态不再感兴趣,只需要解
除关联即可。
在观察者模式中,目标保存了一个对其依赖的对象列表(称为观察者),并在自身状态发生变化时通知这些观察者。目标所采用的通知方式是广播。观察者如果不想再被提醒,可以把自己从列表中移除。理解了这些,我们可以对该模式中的参与者做出如下定义。
目标(Subject):保存观察者列表,拥有可以用来添加、删除和更新观察者的方法。
观察者(Observer):为那些需要在目标状态发生变化时得到提醒的对象提供接口。
JavaScript 的 Model-View-*模式
ECMAScript6(ES6)中的一些重要的新特性
ES6标准于2015年发布。
一款功能最完备,也是最流行的ES6转换编译器(transpiler)Bable(https://babeljs.io/)。
var 与 let,以及ES6中的块级作用域
如果声明了一个变量,但是没有给它赋值,那么该变量的类型默认是未定义的。有一件很糟糕的事情,如果没有使用关键字var声明变量,这种变量会成为隐式全局变量(implict global)。
//一个块级作用域在JS中用{}表示,里面放JS代码
{
var m1 = 'm1';
let m2 = 'm2';
console.log(m1);//m1
console.log(m2);//m2
}
console.log(m1);//m1
console.log(m2);//Uncaught ReferenceError: m2 is not defined
ES6中块级作用域还可以用来创建常量,使用关键字const(常量在声明的同时必须初始化。 )。一旦设置好常量的值,就再也不能更改了:
if(true){
const a=1;
console.log(a);
a=100; // a是只读的,对其赋值会引发TypeError
}
块级作用域规则也适用于函数。在块中声明的函数,只能在块级作用域范围内使用(前提是严格模式,即使用"use strict";
)。
默认参数
ES6之前可能会这样设置默认参数:
function sum(a,b){
a = a || 0;
b = b || 0;
return (a+b);
}
console.log(sum(9,9)); //18
console.log(sum(9)); //9
但在ES6中可以这样写:
function sum(a=0, b=0){
return (a+b);
}
console.log(sum(9,9)); //18
console.log(sum(9)); //9
模板字符串
拼接字符串,在ES6中可以这样:
function SuperLogger(level, clazz, msg){
console.log(level+": Exception happened in class:"+clazz+" - Exception:"+ msg);
}
//ES6中可以这样:
function SuperLogger(level, clazz, msg){
console.log(`${level} : Exception happened in class: ${clazz} - Exception: {$msg}`);
}
另外``包起来的内容之中,允许多行字符串,即你可以实现四行古诗的log打印效果。
for of
循环
for/in 语句用于循环对象属性。循环中的代码每执行一次,就会对数组的元素或者对象的属性进行一次操作。
注意,不要使用 for/in 语句来循环数组的索引,你可以使用 for 语句替代。
来比较一下for..of和for..in:
var person = {fname:"John", lname:"Doe", age:25};
var text = "";
var x;
for (x in person) {
text += person[x] + " ";
}
console.log(text);//John Doe 25
var list = ['Sunday','Monday','Tuesday'];
for (let i in list){
console.log(i); //0 1 2
}
for (let i of list){
console.log(i); //Sunday Monday Tuesday
}
可以看到,使用for..in循环的话,可以遍历数组list的索引,而for..of循环可以遍历数组list的值。
箭头函数
先来看看箭头函数的样子:
// 传统函数
function multiply(a,b) {
return a*b;
}
// 箭头函数
var multiply = (a,b) => a*b;
console.log(multiply(1,2)); //2
//箭头函数,只有一个参数,参数列表可以不写括号
var f1 = x => console.log("Just X");
f1(); //Just X
// 没有参数,单条语句
var f5 = () => 2*2;
console.log(f5()); //4
//IIFE
console.log(( x => x * 3 )( 3 )); // 9
箭头函数的定义由参数列表[零个或多个参数,如果不止一个参数,要在参数外加上(..)]、=>符号以及随后的函数体所组成。如果函数体中不止一个表达式,需要在函数体外加上{ .. }。如果只有一个表达式的话,可以忽略周围的{ .. },在表达式之前会有一个隐含的换行。
重要的是要记住普通函数参数的所有特点都可以用于箭头函数,这包括默认值、解构以及rest参数。
DOM操作和事件
文档对象模型(Document Object Model,DOM)和 JavaScript事件模型。
什么是 HTML DOM?
HTML DOM 是 HTML 的标准对象模型和编程接口。它定义了:
- 作为对象的 HTML 元素
- 所有 HTML 元素的属性
- 访问所有 HTML 元素的方法
- 所有 HTML 元素的事件
换言之:HTML DOM 是关于如何获取、更改、添加或删除 HTML 元素的标准。
通过 HTML DOM,JavaScript 能够访问和改变 HTML 文档的所有元素。通过这个对象模型,JavaScript 获得创建动态 HTML 的所有力量:
- JavaScript 能改变页面中的所有 HTML 元素
- JavaScript 能改变页面中的所有 HTML 属性
- JavaScript 能改变页面中的所有 CSS 样式
- JavaScript 能删除已有的 HTML 元素和属性
- JavaScript 能添加新的 HTML 元素和属性
- JavaScript 能对页面中所有已有的 HTML 事件作出反应
- JavaScript 能在页面中创建新的 HTML 事件
事件传播
如果某个元素及其祖先都注册了同一事件的事件处理程序,哪个事件处理程序会先被触发?考虑下面的示意图:
答案完全依赖于浏览器。说到浏览器,自然有两个阵营:网景与微软。
网景认为第一个触发的事件应该是Element1上的onClick,这种事件顺序称为事件捕获。
微软认为第一个触发的事件应该是Element2上的onClick,这种事件顺序称为事件冒泡。
这是两种完全相反的浏览器事件处理观点及实现。为了结束这种混乱,万维网协会(W3C)采用了一种明智的中间路线。在新的模型中,事件先进入捕获阶段,到达目标元素之后再向上进入冒泡阶段。在这种标准行为中,你可以选择将事件处理程序注册到捕获阶段或是冒泡阶段。如果addEventListener()的最后一个参数是true,则事件处理程序注册在捕获阶段;如果是false,则注册在冒泡阶段。
有时候,在事件已经被子元素处理过之后,你并不想让其父元素再经手了。你可以在事件对象上调用stopPropagation()方法,阻止事件处理程序进一步接收该事件。有些事件有与之关联的默认行为。例如当你点击了一个URL链接,会被带向链接的目标地址。JavaScript事件处理程序会在默认行为被执行前调用。你可以在事件对象上调用preventDefault()方法来阻止执行默认行为。
jQuery 事件处理及传播
jQuery总是将事件处理程序注册在冒泡阶段。这意味着最具体的元素能够最先响应事件。
采用.on()方法的一个主要优势在于可以一次性绑定多个事件。你可以传入多个以空格分隔的事件名称。考虑下面的例子:
$('#inputBoxUserName').on('focus blur', function() {
console.log( Handling Focus or blur event' );
});
//你可以为多个事件添加多个事件处理程序
$( "#heading" ).on({
mouseenter: function() {
console.log( "mouse entered on heading" );
},
mouseleave: function() {
console.log( "mouse left heading" );
},
click: function() {
console.log( "clicked on heading" );
}
});
在jQuery 1.7中,所有事件都是通过on()方法绑定的,即便你调用了如click()之类的辅助方法。在内部,jQuery会将这些调用都映射到on()方法。正因为如此,一般推荐使用on()方法来确保一致性以及更快的执行速度。
针对动态生成的元素添加事件
<html>
<body>
<div id="container">
<ul id="list">
<li><a href="http://google.com">Google</a></li>
<li><a href="http://myntra.com">Myntra</a></li>
<li><a href="http://bing.com">Bing</a></li>
</ul>
</div>
</body>
</html>
//如果新的URL作为列表项加入,那么之前的事件处理程序是无法绑定到新的列表项上的。
$( "#list a" ).on( "click", function( event ) {
console.log( $( this ).text() ); //$( this )可以获得触发事件的DOM对象的引用
});
我们可以创建一个委托事件:
//针对可能有动态生成的元素绑定事件
$( "#list" ).on( "click", "a", function( event ) {
console.log( $( this ).text() );
});
jQuery时间处理函数中的 event:
jQuery会给所有的事件回调程序传入一个事件对象:event,他可以用来阻止默认行为和向上冒泡:
$( "#loginform" ).on( "submit", function( event ) {
// 阻止表单的默认的提交行为
event.preventDefault();
// 阻止事件在DOM树中向上冒泡,停止所有的委托
event.stopPropagation();
});
推荐的JS库
日期时间处理
对于任何需要对日期和时间进行精细控制的应用程序,推荐使用
Moment.js(https://github.com/moment/moment)
这些库为你简化了大量的重复性任务,有助于你将精力集中在其他重要的方面。
数据处理
一个JS数据处理(数组、对象、map、set)的库
Underscore.js:jashkenas/underscore: JavaScript's utility _ belt (github.com)
疑问&模糊点
1、this 在function中的意义
当函数被调用时,除了在调用时明确给出的那些参数,还有一个叫作this的隐式参数也会被传入函数。它指向一个与此次函数调用相关联的对象,该对象被称为函数上下文(function context)。
2、new 后面跟一个自定义函数,是什么鬼
?
3、JS的IIFE
IIFE: Immediately Invoked Function Expression,意为立即调用的函数表达式,也就是说,声明函数的同时立即调用这个函数。详见:说一说JS的IIFE - Yiven - 博客园 (cnblogs.com)
参考代码:
for (var i=1; i<=5; i++) { (function(j){ setTimeout( function delay(){ console.log( j ); }, j*100); })( i ); }//在控制台中以100ms的间隔分别打印出1、2、3、4、5
3、JS中定义的函数,为什么函数名可以当做对象来使用
4、下面圈住的那行代码,为啥能影响上一行中的window?因为打印的时候,palyer方法并没有执行啊?
更新于:2023-4-1
标签:function,精粹,console,log,JavaScript,JS,var,函数 From: https://www.cnblogs.com/idasheng/p/17278896.html