JS特性
- 解释性语言,不需要提前编译
- 动态类型的语言
- 按照从上往下的顺序一行一行执行的
JavaScript代码能放在哪里?
- html文件中的head标签里,可以写一个script标签,里面放上JS代码
- 放在button的onclick属性中,点击按钮的时候,代码就会执行
- 放在a标签的href中,点击超链接是,代码就会执行
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script type="text/javascript">
console.log("This is javascript");
</script>
</head>
<body>
<button onclick="alert('You clicked me!');">click button</button>
<a href="javascript:alert('Greeting from a tag!');">click hypertext</a>
</body>
</html>也可以将javascript代码写入一个外部的js文件当中,然后通过script标签进行导入。写入外部文件的好处是不同的html页面都可以引用它,也可以利用到浏览器的缓存机制。是比较推荐的一种方式。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script type="text/javascript" src="script.js"></script>
</head>
<body>
</body>
</html>注意:script标签一旦用于引入外部文件,就不能在标签体里面写js代码了,即使写了浏览器也会忽略的。如果头铁,非得写,那就在新建一个script标签。
注释
注释有单行注释或者多行注释
/*
这是一个
多行
注释
*/
// 这是一个单行注释数据类型
六种基本的数据类型:
- String
- Number
- Boolean
- Null
- Undefined
- Object
前五种为基本数据类型,而Object属于引用数据类型。
判断一个变量的类型,可以用typeof来检查。
var a = 123;
console.log((typeof a) == 'number'); // true
var b = "hello";
console.log((typeof b) == 'string'); // trueNumber.MAX_VALUE -> JS数字的最大值,超过该值就会显示Infinity。
NaN -> Not a number,如果用typeof来检查,还是会显示为number。
Number.MIN_VALUE -> 0以上的最小的数字,表示正的最小数。
null专门用来表示一个为空的对象。如果用typeof检查,会返回“object”。
当申明一个变量但不给变量赋值时就是undefined,用typeof检查就是”undefined”。
强制类型转换
将其他数据类型转换为String
- 调用被转换类型的toString()方法。该方法不会影响到原变量,而是将转换的结果返回。但是要注意,null和undefined没有该方法。
- 调用String()函数并将被转换的数据作为参数传递给该函数。对于Number和Boolean底层还是调用的toString()方法,将null转换为”null”,将undefined转换为“undefined”。
将其他数据类型转换为Number
- 使用Number()函数并将被转换的数据作为参数传递给该函数。如果是纯数字字符串的话,直接将其转换为数字。如果字符串中有非数字内容,则转换为NaN。如果字符串是一个空串或者全是空格的字符串,则转换为0.
- 布尔值转Number,true转为1,false转为0
- null转为0
- Undefined转为NaN
- parseInt()将字符串转换为整数,parseFloat()将字符串转换为浮点数。他们都是从左往右读,一直读到非数字为止。如果传递的是非字符串,那就先转换为字符串然后再处理。
- 十六进制的数字,用0x开头,八进制用0开头,二进制用0b开头
- parseInt()的第二个参数为指定第一个参数是几进制的
逻辑运算符
取反 !
用!对非布尔值进行取反操作,则先将该值转换为布尔值,再进行取反操作。
与运算
对非布尔值进行运算的时候,会将其转换为布尔值,然后再运算,并返回原值
var a = 1 && 2; // a就是2,因为与运算如果左边为真,右边的也要看
var b = 0 && 2; // b就是0,因为与运算左边为0,也就是false,所以就不用看右边了或运算
如果第一个值为true,则直接返回第一个值,如果第一个值为false,则返回第二个值
var a = 1 || 2 // a就是1,因为或运算如果左边为true,就不看右边了
var b = 0 || 3 // b就是3,因为或运算如果左边为false,得看右边全等/不全等
===和==类似,但不同的是全等不会做自动类型转换,如果类型不同,直接返回false。不全等同理。
流程控制语句
if…else if…else if…else
该语句中,只有一个代码块会被执行,一旦代码块执行了,则直接结束语句。
switch…case…
在执行时会依次将case后的表达式的值和switch后的条件表达式的值进行全等比较,如果比较结果为true,则从当前case处开始执行代码。当前case后的所有代码都会执行,因此我们可以在case的后面跟一个break关键字来跳出switch语句。如果比较结果为false,则继续向下比较。如果所有的比较结果都为false,则执行default里的语句。
对象
- 如果要使用特殊的属性名,如数字123,不能采用
.的方式来操作,而是需要使用另一种方式:
对象[“属性名”]=属性值
读取的时候也要采用这种方式。使用[]这种形式去操作属性,更加的灵活。在[]中可以直接传递一个变量,这样变量值是多少就会读取那个属性。 - del用来删除对象里的属性,in可以用来判断某个属性是否在一个对象里,返回true就有,没有则返回false,语法为 “属性名” in 对象
作用域
全局作用域
- 直接编写在script标签中的JS代码,都在全局作用域
- 全局作用域在页面打开时创建,在页面关闭时销毁
- 在全局作用域中有一个全局对象window,我们可以直接使用
- 在全局作用域中,创建的变量都会作为window对象的属性保存,创建的函数都会作为window对象的方法保存
变量的声明提前
使用var关键字声明的变量,会在所有的代码执行之前被声明,但是不会被赋值。但是如果声明变量时不使用var关键字,则变量不会被提前声明。
函数的声明提前
使用函数声明形式创建的函数
function function() {}它会在所有的代码执行之前就被创建,所以我们可以在函数声明之前就调用函数。
使用函数表达式创建的函数,不会被声明提前,所以不能在声明前调用。
函数作用域
- 调用函数时创建函数作用域,函数执行完毕以后,函数作用域销毁
- 每调用一次函数就会创建一个新的函数作用域,他们之间时相互独立的
- 在函数作用域中可以访问到全局作用域的变量,但在全局作用域中无法访问函数作用域的变量
- 当在函数作用域操作一个变量时,它会先在自身作用域中寻找,如果有就直接使用,如果没有就向上一级作用域中寻找,知道找到全局作用域(例如方法A里又定义了个方法B, 那方法B自身找不到时,就去A里找,A里再找不到就去全局找)。如果全局都找不到的话,就会报ReferenceError。
- 在函数中要访问全局变量可以使用window对象
- 在函数作用域中也有声明提前的特性,也就是说用var声明的变量,会在函数中所有的代码被执行前被声明,函数声明也会在函数中所有的代码执行之前执行。
- 在函数中,不使用var声明的变量都会成为全局变量,也就是该变量会被加到window对象上
- 定义形参就相当于在函数作用域中声明了变量
this
解析器在调用函数时每次都会向函数内部传递一个隐藏的参数this,指向一个对象,这个对象我们称为函数执行的上下文对象。根据函数的调用方式不同,this会指向不同的对象。
- 以函数的形式调用时,this永远都是window
- 以方法的形式调用时,this就是调用方法的那个对象
- 以构造函数的形式调用时,this就是新创建的那个对象
- 使用call和apply调用时,this是指定的那个对象
构造函数
构造函数就是一个普通的函数,创建方式和普通函数没有区别,不同的是构造函数习惯上首字母大写。
构造函数和普通函数的区别就是调用方式的不同。普通函数是直接调用,而构造函数需要使用new关键字来调用。
构造函数的执行流程:
- 立刻创建一个新的对象
- 将新建的对象设置为函数中的this,在构造函数中可以使用this来引用新建的对象
- 逐行执行函数中的代码
- 将新建的对象作为返回值返回
使用同一个构造函数创建的对象,我们称之为一类对象,也将一个构造函数称为一个类。我们将通过一个构造函数创建的对象,称为是该类的实例。
原型prototype
我们所创建的每一个函数,解析器都会向函数中添加一个属性prototype,这个属性对应着一个对象,这个对象就是我们所谓的原型对象。
如果函数作为普通函数调用那prototype没有任何作用。当函数以构造函数的形式调用时,它所创建的对象中都会有一个隐含的属性,指向该构造函数的原型对象,我们可以通过__proto__来访问该属性。
原型对象就相当于一个公共的区域,所有同一个类的实例都可以访问到这个原型对象,我们可以将对象中共有的内容,统一设置到原型对象中。
当我们访问对象的一个属性或方法时,它会先在对象自身中寻找,如果有则直接使用,如果没有则会去原型对象中寻找,如果找到则直接使用。如果找不到,则接着往原型对象的原型对象中找,直到找到Object对象的原型。
以后我们创建构造函数时,可以将这些对象共有的属性和方法,统一添加到构造函数的原型对象中,这样不用分别为每一个对象添加,也不会影响到全局作用域,就可以使每个对象都具有这些属性和方法了。
数组常用方法
- push:向数组的末尾添加一个或多个元素,并返回数组的新的长度
- pop:删除数组的最后一个元素,并将删除的元素作为返回值返回
- unshift:向数组开头添加一个或多个元素,并返回新的数组长度
- shift: 删除数组的第一个元素,并将被删除的元素作为返回值返回
- forEach: 需要穿进去一个回调函数,浏览器会在回调函数中传递3个参数,分别是当前元素,索引以及数组对象。
- slice: 从数组中提取指定元素,不会改变原数组,而是返回一个新的数组。带两个参数,第一个是开始位置的索引(包含),第二个是结束位置的索引(不包含)。第二个参数可以忽略。索引可以传递负值,表示从后往前数,-1是倒数第一个,-2是倒数第二个。
- splice:会影响到原数组,将指定元素从数组中删除,并将删除的元素作为数组返回。可以带的参数:第一个参数是要删除元素的开始位置,第二个参数是要删除的个数,第三个及以后是要在被删除的开始元素的位置插入新的元素。
- concat: 连接两个或多个数组,并将新的数组返回,不会对原数组产生影响。arr.contact(arr2, arr3, “element1”, 123, true);
- join: 将一个数组转换为一个字符串,该方法不会对原数组产生影响,而是将转换后的字符串作为结果返回。join中可以指定一个字符串作为参数,这个字符串将会成为数组中元素的连接符。如果不指定,默认逗号作为连接符。
- reverse:反转数组,会修改原数组。
- sort: 对原数组进行排序,会影响原数组,默认会按照Unicode编码进行排序。可以在sort中添加一个回调函数,来指定排序规则,回调函数中需要定义两个形参,浏览器会分别使用数组中的元素作为实参去调用回调函数。回调函数如果返回值小于0或等于0,则顺序不变,如果返回值大于1,则元素交换位置。
call和apply方法
这两个方法都是函数对象的方法,需要通过函数对象来调用。当对函数调用call和apply时都会调用函数执行,可以将一个对象作为第一个参数传进去,此时这个对象就会成为函数执行的this。区别为:
- call()方法可以将实参在对象之后依次传递
- apply()方法需要将实参封装到一个数组里统一传递
function fun(a, b) {
console.log("a is: " + a);
console.log("b is: " + b);
}
fun.call(obj, 1, 2)
fun.apply(obj, [1, 2])Arguments
在调用函数时,浏览器每次都会传递两个隐含的函数:
- 函数的上下文对象this
- 封装实参的对象arguments:
- arguments是一个类数组对象,它也可以通过索引来操作数据,也可以获取长度
- 在调用函数时,我们所传递的实参都会在arguments中保存
- arguments.length可以用来获取实参的长度
- 我们即使不定义形参,也可以通过arguments来使用实参,只不过比较麻烦,arguments[0]表示第一个实参,arguments[1]表示第二个实参…
- 它里面还有一个属性callee,对应一个函数对象,就是当前正在执行的函数对象
包装类
JS中为我们提供了3个包装类,通过这3个包装类可以将基本数据类型的数据转换为对象。
- String() 将基本数据类型字符串转换为String对象
- Number() 将基本数据类型数字转换为Number对象
- Boolean() 将基本数据类型布尔值转换为Boolean对象
但在实际开发中,我们一般不用基本数据类型的对象,因为在比较的时候会带来一些不可预期的结果。
当我们对一些基本数据类型的值去调用属性和方法时,浏览器会临时使用包装类将其转换为对象,然后再调用对象的属性和方法。调用完以后,再将其转换为基本数据类型。
正则表达式
用于定义一些字符串的规则,计算机可以根据正则表达式,来检查一个字符串是否符合规则,或者将字符串中符合规则的内容提取出来。
DOM
Document Object Model. JS通过DOM来对HTML文档进行操作,只要理解了DOM就可以随心所欲的操作WEB页面。
文档: 文档表示的就是整个html文档
对象:对象表示将网页中的每一个部分都转换成了一个对象
模型:使用模型来表示对象之间的关系,这样方便我们获取对象

节点
Node: 构成html文档的最基本单元,分为四类:
– 文档节点:整个HTML文档
– 元素节点:HTML文档中的html标签
– 属性节点:元素的属性
– 文本节点:html标签中的文本内容

浏览器为我们提供了 文档节点 对象,作为window的属性,可以在页面中直接使用。文档节点代表的是整个网页。
事件
用户和浏览器的交互行为,比如:点击按钮,鼠标移动,关闭窗口等等。我们可以在事件对应的属性中设置一些js代码,这样当事件被触发时,这些代码将会执行。(不推荐,因为结构和行为耦合了,不方便维护。)我们可以为按钮的对应事件绑定处理函数的形式来相应事件。
var btn = document.getElementById("btn");
btn.onclick = function() {
alert("hello there!");
}文档的加载
浏览器在加载一个页面时,是按照自上而下的顺序加载的,读取到一行就运行一行,如果将script标签写到页面上边,在代码执行时,页面还没有加载。那如何解决呢?有两种方法:
- 将js代码写在body标签的最下面
- 仍然将js代码留在head里,但延迟执行: 为window绑定一个onload事件,会在整个页面加载完成之后才触发,这样我们可以确保我们的代码执行时所有的DOM对象已经加载完毕了。
常用的方法
通过document对象调用的方法和属性:
- getElementById() 通过id属性获取一个元素节点对象
- getElementsByTagName() 通过标签名获取一组元素节点对象
- getElementsByName() 通过name属性获取一组元素节点对象
- getElementsByClassName() 通过class名字来获取一组元素节点对象
- querySelector(): 需要一个选择器的字符串作为参数,可以根据CSS选择器来查询一个元素节点,比如class为“123”的div中的img。缺点是,如果有多个元素满足选择器字符串,那只会返回第一个。
- querySelectorAll(): 和querySelector()不同的是它会返回所有满足条件的元素,并以类数组形式返回。
- body: 对body的引用
- documentElement: html根标签
- all: 代表页面中所有元素 (document.getElementsByTagName(“*”)是相同效果)
元素节点对象上能调用的方法:
- getElementsByTagName() 通过当前节点的指定标签名的后代节点
- childNodes 表示当前节点的所有子节点,包括文本节点。dom标签间的空白会被当成文本节点。
- children 获取当前元素的所有子元素(不包含文本节点)
- firstChild 表示当前节点的第一个子节点
- firstElementChild 表示当前节点的第一个子元素
- lastChild 表示当前节点的最后一个子节点
- parentNode 表示当前节点的父结点
- previousSibling 表示当前节点的前一个兄弟节点
- previousElementSibling 表示当前节点的前一个兄弟元素
- nextSibling 表示当前节点的后一个兄弟节点
innerHTML和innerText的唯一区别是有没有html元素还是纯文本。
JS修改样式
语法:元素.style.样式名=样式值
注意:如果css的样式名中含有-,这种名称在js中是不合法的,比如background-color,需要将这种样式名修改为驼峰命名法,去掉-,然后将-后的字母大写。
我们通过style属性设置的样式都是内联样式,而内联样式有较高的优先级,所以通过js修改的样式往往会立即显示。但是如果在样式中写了!important则此时样式会有最高优先级,即使通过js也不能覆盖该样式,此时会导致js修改样式失效,所以尽量不要为样式添加!important
通过style属性设置和读取的都是内联样式,无法读取样式表中的样式。
获取元素的当前显示的样式(注意:只有IE支持,所以这个也没啥用)
语法:元素.currentStyle.样式名
它可以用来读取当前元素正在显示的样式。如果当前元素没有设置该样式,则获取它的默认值
替代currentStyle的话,用getComputedStyle()方法来获取当前元素的样式。这个方法是window的方法,可以直接使用。需要两个参数,第一个是要获取样式的元素,第二个是一个伪元素,一般都传null。该方法会返回一个对象,对象中封装了当前元素对应的样式。可以通过对象.样式名来读取样式,如果获取的样式没有设置,则会获取到真实的值,而不是默认值。比如:没有设置width,它不会获取到auto,而是一个真实的长度。
通过currentStyle和getComputerdStyle()读取到的样式都是只读的,不能修改。如果要修改必须通过style属性。
事件的冒泡
所谓的冒泡指的就是事件的向上传导,当后代元素上的事件被触发时,其祖先元素的相同事件也会被触发。在开发中大部分冒泡事件都是有用的。如果不希望事件向上冒泡,可以通过事件对象来取消冒泡(将事件对象的cancelBubble设置为true)。
事件的委派:将事件统一绑定给元素的共同祖先元素,这样当后代元素上的事件触发时,会一直冒泡到祖先元素,从而通过祖先元素的相应函数来处理事件。
事件的绑定
component.onclick = function() {}可以为元素绑定响应函数,addEventListener方法也可以为元素绑定响应函数。带两个参数,第一个是事件的字符串,但记住不能加on,比如直接是click而不是onclick。第二个参数是回调函数,当事件触发时,该函数会被调用。第三个参数是是否在捕获阶段触发事件,需要一个布尔值,一般都传false。addEventListener可以为元素的相同事件绑定多个回调函数。
事件的传播
事件传播分成三个阶段:
- 捕获阶段:在捕获阶段时从最外层的祖先元素,向目标元素进行事件的捕获,但是默认此时不会触发事件
- 目标阶段:事件捕获到目标元素,捕获结束,开始在目标元素上触发事件
- 冒泡阶段:事件从目标元素向其祖先元素传递,依次触发祖先元素上的事件
如果希望在捕获阶段就触发事件,可以将addEventListener方法的第三个参数设置为true。但一般情况在我们不会希望在捕获阶段触发事件,所以这个参数一般都是false.
BOM
- 全称:浏览器对象模型
- BOM可以使我们通过JS来操作浏览器
- 在BOM中为我们提供了一组对象,用来完成对浏览器的操作
- BOM对象
- Window: 代表的是整个浏览器的窗口,同时window也是网页中的全局对象
- Navigator: 代表当前浏览器的信息,通过该对象可以识别不同的浏览器。由于历史原因,该对象中大部分属性已经不能帮助我们识别浏览器了,一般我们只会使用userAgent来判断浏览器信息(但从ie11开始已经不能判断其是否是ie浏览器了,所以基本上我们也不能通过userAgent来识别一个浏览器是否为ie了)
- Location: 代表当前浏览器的地址栏信息,或者操作浏览器跳转页面。如果直接将location属性修改为一个完整路径或者相对路径,则我们页面会自动跳转到该路径,并且会生成相应的历史记录。location.assign()和直接修改location的值效果一样。location.reload()用于重新加载当前页面,作用和刷新按钮一样。如果在方法中传递一个true作为参数,则会强制清空缓存刷新页面。replace()可以使用一个新的页面替换当前页面,调用完毕也会跳转页面,但不会生成历史记录,不能使用回退按钮回退。
- History: 代表浏览器的历史记录,可以通过该对象来操作浏览器的历史记录。但由于隐私原因,该对象不能获取到具体的历史记录,只能操作浏览器向前或向后翻页,而且该操作只在当次访问时有效。history.back()往后退一页,history.forward()往前进一页, history.go()带一个整数参数,1表示往前进一页,-1表示往后退一页。
- Screen: 代表用户的屏幕信息,通过该对象可以获取到用户的显示器相关信息(很少用到)
- 这些BOM对象在浏览器中都是作为window对象的属性保存的,可以通过window对象来使用,也可以直接使用
setInterval/
- setInterval():定时调用,可以将一个函数,每隔一段时间执行一次,参数有两个,第一个是要执行的callback function,第二个是每次调用间隔的事件,单位是毫秒。返回值是一个Number类型的数据,用来作为定时器的唯一标识。
- clearInterval():关闭定时器,需要传进去定时器的标识作为参数。
