JavaScript的组成
ECMAScript - JavaScript的核心
定义了javascript的语法规范
JavaScript的核心,描述了语言的基本语法和数据类型,ECMAScript是一套标准,定义了一种语言的标准与具体实现无关BOM - 浏览器对象模型
一套操作浏览器功能的API
通过BOM可以操作浏览器窗口,比如:弹出框、控制浏览器跳转、获取分辨率等DOM - 文档对象模型
一套操作页面元素的API
DOM可以把HTML看做是文档树,通过DOM提供的API可以对树上的节点进行操作。
BOM
BOM(Browser Object Model) 是指浏览器对象模型,浏览器对象模型提供了独立于内容的、可以与浏览器窗口进行互动的对象结构。BOM由多个对象组成,其中代表浏览器窗口的Window对象是BOM的顶层对象,其他对象都是该对象的子对象。
比如:刷新浏览器、后退、前进、在浏览器中输入URL等
BOM的顶级对象window
window是浏览器的顶级对象,当调用window下的属性和方法时,可以省略window。
注意:window下一个特殊的属性 window.name
对话框
- alert()
- prompt()
- confirm()
页面加载事件
window.onload = function () {
// 当页面加载完成执行
// 当页面完全加载所有内容(包括图像、脚本文件、CSS 文件等)执行
}
window.onunload = function () {
// 当用户退出页面时执行
}
定时器
-
setTimeout()
和clearTimeout()
,在指定的毫秒数到达之后执行指定的函数,只执行一次。
// 创建一个定时器,1000毫秒后执行,返回定时器的标示
var timerId = setTimeout(function () {
console.log('Hello World');
}, 1000);
// 取消定时器的执行
clearTimeout(timerId);
-
setInterval()
和clearInterval()
定时调用的函数,可以按照给定的时间(单位毫秒)周期调用函数。
// 创建一个定时器,每隔1秒调用一次
var timerId = setInterval(function () {
var date = new Date();
console.log(date.toLocaleTimeString());
}, 1000);
// 取消定时器的执行
clearInterval(timerId);
location对象
location对象是window对象下的一个属性,使用的时候可以省略window对象。
location可以获取或者设置浏览器地址栏的URL
history对象
- back()
- forward()
- go()
navigator对象
userAgent
通过userAgent可以判断用户浏览器的类型platform
通过platform可以判断浏览器所在的系统平台类型
DOM
文档对象模型(Document Object Model,简称DOM),是W3C组织推荐的处理可扩展标志语言的标准编程接口。在网页上,组织页面(或文档)的对象被组织在一个树形结构中,用来表示文档中对象的标准模型就称为DOM。
DOM经常进行的操作
-
获取元素
- 根据id获取元素
var div = document.getElementById('main');
- 根据标签名获取元素
var divs = document.getElementsByTagName('div');
- 根据name获取元素
var inputs = document.getElementsByName('hobby');
- 根据类名获取元素
var mains = document.getElementsByClassName('main');
- 根据选择器获取元素
var text = document.querySelector('#text'); 或 var texts = document.querySelectorAll('.text');
-
动态创建元素
- document.write()
document.write('新设置的内容<p>标签也可以生成</p>');
- innerHTML
var box = document.getElementById('box'); box.innerHTML = '新内容<p>新标签</p>';
- document.createElement()
var div = document.createElement('div'); document.body.appendChild(div);
对元素进行操作(设置其属性或调用其方法)
-
事件(什么时机做相应的操作)
-
事件三要素
- 事件源:触发(被)事件的元素
- 事件类型:事件的触发方式(例如鼠标点击或键盘点击)
- 事件处理程序:事件触发后要执行的代码(函数形式)
-
事件的三个阶段
- 捕获阶段
- 当前目标阶段
- 冒泡阶段
事件对象.eventPhase属性可以查看事件触发时所处的阶段。
-
innerHTML和innerText之间的区别
innerHTML原封不动的把div元素里面的内容呈现出来包括所有的空白、标签;而innerText只呈现标签中的文本内容。
表单元素属性
- value 用于大部分表单元素的内容获取(option除外)
- type 可以获取input标签的类型(输入框或复选框等)
- disabled 禁用属性
- checked 复选框选中属性
- selected 下拉菜单选中属性
自定义属性操作
- getAttribute() 获取标签行内属性
- setAttribute() 设置标签行内属性
- removeAttribute() 移除标签行内属性
- 与element.属性的区别: 上述三个方法用于获取任意的行内属性。
样式操作
- 使用style方式设置的样式显示在标签行内
var box = document.getElementById('box');
box.style.width = '100px';
box.style.height = '100px';
box.style.backgroundColor = 'red';
注意: 通过样式属性设置宽高、位置的属性类型是字符串,需要加上px。
类名操作
- 修改标签的className属性相当于直接修改标签的类名。
var box = document.getElementById('box');
box.className = 'clearfix';
节点操作
var body = document.body;
var div = document.createElement('div');
body.appendChild(div);
var firstEle = body.children[0];
body.insertBefore(div,firstEle);
body.removeChild(firstEle);
var text = document.createElement('p');
body.replaceChild(text, div);
节点层级
var box = document.getElementById('box');
console.log(box.parentNode);
console.log(box.childNodes);
console.log(box.children);
console.log(box.nextSibling);
console.log(box.previousSibling);
console.log(box.firstChild);
console.log(box.lastChild);
注意:
- childNodes和children的区别,childNodes获取的是子节点,children获取的是子元素;
- nextSibling和previousSibling获取的是节点,获取元素对应的属性是nextElementSibling和previousElementSibling获取的是元素;
- nextElementSibling和previousElementSibling有兼容性问题,IE9以后才支持。
总结
-
节点操作,方法
- appendChild()
- insertBefore()
- removeChild()
- replaceChild()
-
节点层次,属性
- parentNode
- childNodes
- children
- nextSibling/previousSibling
- firstChild/lastChild
常用的鼠标和键盘事件
- onmouseup 鼠标按键放开时触发
- onmousedown 鼠标按键按下触发
- onmousemove 鼠标移动触发
- onkeyup 键盘按键按下触发
- onkeydown 键盘按键抬起触发
事件对象的属性和方法
- event.type 获取事件类型
- clientX/clientY 所有浏览器都支持,窗口位置
- pageX/pageY IE8以前不支持,页面位置
- event.target || event.srcElement 用于获取触发事件的元素
- event.preventDefault() 取消默认行为
属性成员的搜索原则:原型链
每当代码读取某个对象的某个属性时,都会执行一次搜索,目标是具有给定名字的属性
- 搜索首先从对象实例本身开始;
- 如果在实例中找到了具有给定名字的属性,则返回该属性的值;
- 如果没有找到,则继续搜索指针指向的原型对象,在原型对象中查找具有给定名字的属性;
- 如果在原型对象中找到了这个属性,则返回该属性的值。
总结:
- 先在自己身上找,找到即返回;
- 自己身上找不到,则沿着原型链向上查找,找到即返回;
- 如果一直到原型链的末端还没有找到,则返回
undefined
。
更简单的原型语法
为减少不必要的输入,更常见的做法是用一个包含所有属性和方法的对象字面量来重写整个原型对象:
function Person (name, age) {
this.name = name
this.age = age
}
Person.prototype = {
type: 'human',
sayHello: function () {
console.log('我叫' + this.name + ',我今年' + this.age + '岁了')
}
}
在该示例中,我们将 Person.prototype
重置到了一个新的对象。
这样做的好处就是为 Person.prototype
添加成员简单了,但是也会带来一个问题,那就是原型对象丢失了 constructor
成员。
所以,我们为了保持 constructor
的指向正确,建议的写法是:
function Person (name, age) {
this.name = name
this.age = age
}
Person.prototype = {
constructor: Person, // => 手动将 constructor 指向正确的构造函数
type: 'human',
sayHello: function () {
console.log('我叫' + this.name + ',我今年' + this.age + '岁了')
}
}
继承
方式一:构造函数的属性继承:借用构造函数
function Person (name, age) {
this.type = 'human'
this.name = name
this.age = age
}
function Student (name, age) {
// 借用构造函数继承属性成员
Person.call(this, name, age)
}
var s1 = Student('张三', 18)
console.log(s1.type, s1.name, s1.age) // => human 张三 18
构造函数的原型方法继承:拷贝继承(for-in)
function Person (name, age) {
this.type = 'human'
this.name = name
this.age = age
}
Person.prototype.sayName = function () {
console.log('hello ' + this.name)
}
function Student (name, age) {
Person.call(this, name, age)
}
// 原型对象拷贝继承原型对象成员
for(var key in Person.prototype) {
Student.prototype[key] = Person.prototype[key]
}
var s1 = Student('张三', 18)
s1.sayName() // => hello 张三
方式二:原型继承
function Person (name, age) {
this.type = 'human'
this.name = name
this.age = age
}
Person.prototype.sayName = function () {
console.log('hello ' + this.name)
}
function Student (name, age) {
Person.call(this, name, age)
}
// 利用原型的特性实现继承
Student.prototype = new Person()
var s1 = Student('张三', 18)
console.log(s1.type) // => human
s1.sayName() // => hello 张三
call、apply、bind
call
call()
方法调用一个函数, 其具有一个指定的 this
值和分别地提供的参数(参数的列表)。
注意: 该方法的作用和 apply()
方法类似,只有一个区别,就是 call()
方法接受的是若干个参数的列表,而 apply()
方法接受的是一个包含多个参数的数组。
语法:
fun.call(thisArg[, arg1[, arg2[, ...]]])
参数:
-
thisArg
- 在 fun 函数运行时指定的 this 值
- 如果指定了 null 或者 undefined 则内部 this 指向 window
-
arg1, arg2, ...
- 指定的参数列表
apply
apply()
方法调用一个函数, 其具有一个指定的 this
值,以及作为一个数组(或类似数组的对象)提供的参数。
注意:该方法的作用和 call()
方法类似,只有一个区别,就是 call()
方法接受的是若干个参数的列表,而 apply()
方法接受的是一个包含多个参数的数组。
语法:
fun.apply(thisArg, [argsArray])
参数:
thisArg
argsArray
apply()
与 call()
非常相似,不同之处在于提供参数的方式。
apply()
使用参数数组而不是一组参数列表。例如:
fun.apply(this, ['eat', 'bananas'])
bind
bind() 函数会创建一个新函数(称为绑定函数),新函数与被调函数(绑定函数的目标函数)具有相同的函数体(在 ECMAScript 5 规范中内置的call属性)。
当目标函数被调用时 this 值绑定到 bind() 的第一个参数,该参数不能被重写。绑定函数被调用时,bind() 也接受预设的参数提供给原函数。
一个绑定函数也能使用new操作符创建对象:这种行为就像把原函数当成构造器。提供的 this 值被忽略,同时调用时的参数被提供给模拟函数。
语法:
fun.bind(thisArg[, arg1[, arg2[, ...]]])
参数:
- thisArg
- 当绑定函数被调用时,该参数会作为原函数运行时的 this 指向。当使用new 操作符调用绑定函数时,该参数无效。
- arg1, arg2, ...
- 当绑定函数被调用时,这些参数将置于实参之前传递给被绑定的方法。
返回值:
返回由指定的this值和初始化参数改造的原函数拷贝。
示例1:
this.x = 9;
var module = {
x: 81,
getX: function() { return this.x; }
};
module.getX(); // 返回 81
var retrieveX = module.getX;
retrieveX(); // 返回 9, 在这种情况下,"this"指向全局作用域
// 创建一个新函数,将"this"绑定到module对象
// 新手可能会被全局的x变量和module里的属性x所迷惑
var boundGetX = retrieveX.bind(module);
boundGetX(); // 返回 81
示例2:
function LateBloomer() {
this.petalCount = Math.ceil(Math.random() * 12) + 1;
}
// Declare bloom after a delay of 1 second
LateBloomer.prototype.bloom = function() {
window.setTimeout(this.declare.bind(this), 1000);
};
LateBloomer.prototype.declare = function() {
console.log('I am a beautiful flower with ' +
this.petalCount + ' petals!');
};
var flower = new LateBloomer();
flower.bloom(); // 一秒钟后, 调用'declare'方法
小结
- call 和 apply 特性一样
- 都是用来调用函数,而且是立即调用
- 但是可以在调用函数的同时,通过第一个参数指定函数内部
this
的指向 - call 调用的时候,参数必须以参数列表的形式进行传递,也就是以逗号分隔的方式依次传递即可
- apply 调用的时候,参数必须是一个数组,然后在执行的时候,会将数组内部的元素一个一个拿出来,与形参一一对应进行传递
- 如果第一个参数指定了
null
或者undefined
则内部 this 指向 window
- bind
- 可以用来指定内部 this 的指向,然后生成一个改变了 this 指向的新的函数
- 它和 call、apply 最大的区别是:bind 不会调用
- bind 支持传递参数,它的传参方式比较特殊,一共有两个位置可以传递
- 在 bind 的同时,以参数列表的形式进行传递
- 在调用的时候,以参数列表的形式进行传递
- 那到底以谁 bind 的时候传递的参数为准呢还是以调用的时候传递的参数为准
- 两者合并:bind 的时候传递的参数和调用的时候传递的参数会合并到一起,传递到函数内部