JavaScript
(参考了《JavaScript高级程序设计》和部分知乎答案)
作用域
指可访问变量、对象、函数的集合。
局部作用域:在函数内声明,只能在函数内部访问
全局作用域:在函数外定义,所有脚本和函数均可使用(函数内赋值但未声明,仍为全局变量)
变量生命周期:全局变量在页面关闭后销毁,局部变量在函数执行完毕后销毁
ps:在HTML中所有数据属于window对象
数据类型
原始数据类型:栈,占据空间小,大小固定
String
Number
Boolean
Object
Function
不含值的数据类型:
Undefined:不存在的值,或变量被声明了但未赋值
Null:一个对象被定义了,但是为空值(没有任何属性和方法)
引用对象类型:堆,占据空间大,大小不固定(在栈中储存了指针,指向堆中的起始位置)
Object
Date
Array
boolean类型转换
数据类型 | true | false |
---|---|---|
Boolean | true | false |
String | 任何非字符串 | ""(空) |
Number | 任何非零数字值 | 0和NaN |
Object | 任何对象 | Null |
Undefined | n/a(不适用) | underfined |
例子:
var message = "Hello world!";
if (message) {
alert("Value is true");
}
正则表达式
由一个字符串序列形成的搜索模式。
如:
var str = "Visit W3cSchool!";
var n = str.search(/W3cSchool/i);
//n=6 (不区分大小写)
变量提升
函数声明和变量声明会被解释器自动提升到方法体的最顶部
var x;
但是初始化的变量不会提升
var x = 5;
x = 5
为避免问题的出现,通常在作用域开始前声明变量
注意:函数声明也会自动提升,而传递给初始化变量的函数则不会
function fn () {} //提升
var fn = function () {} //不提升
函数调用的四种方法
1.方法调用模式
var myobject = {
value: 0,
inc: function() {
alert(this.value);
}
}
myobject.inc();
//this指向myobject
2.函数调用模式
var add = function(a,b) {
alert(this);
return a+b;
}
var sum = add(3,4);
arert sum;
3.构造器调用模式(摒弃)
4.apply调用
var arr = [10,20];
var sum = add.apply(myobject,arr);
aleat(sum);
原型链
当从一个对象那里读取属性或调用方法时,如果该对象不存在这样的属性或方法,就会去自己关联的prototype对象那里寻找,直到找到或追溯过程结束为止。(即对象的属性和方法追溯机制)
”读“属性会沿着原型链搜索,”新增“属性时则不会去看原型链
(obj)
name:'obj' valueOf
__proto__ ——> toString
constructor
...
__proto__ ——> null
闭包
先看两个典型例子
function foo () {
var local = 1;
function bar () {
local++;
return local;
}
return bar;
}
var func = foo();
func(); //2
func(); //3
bar函数调用了外层函数的local变量,函数运行完毕后local仍然保存在内存中,所以每次运行函数后local都会增加1
var add =(function () {
var counter = 0;
return function () {
return counter += 1;
}
})();
add(); //1
add(); //2
add(); //3
和上一个例子的原理是一样的,内层函数调用了外层函数的全局变量count导致其始终存在于内存中,而内部函数的存在也依赖于外层函数,导致其也始终再内存中,不会在调用结束后被垃圾回收机制回收。
这就叫闭包,一句话概括:闭包就是函数和函数内部能访问到的变量的总和。
或者说,如果存在有权访问另一个函数作用域中变量的函数,那就构成了闭包。
它提供了一中间接的方式能够访问到函数内部的数据,有以下两种情况:
1.函数作为返回值
2.函数作为参数传递
正确运用闭包由一个好处,就是让一些数据更安全,只能通过特定的接口来访问。
当然,有时候闭包也会导致一些问题,如下面的例子:
for (var i=0; i<10; i++) {
arr[i] = function() {
return i;
}
}
arr[0]; //10
arr[6]; //10
function内访问了外部变量i,构成了一个闭包,我们先不写内部的i,每次赋值的结果如下
arr[0] = function() { return i };
arr[1] = function() { return i };
...
arr[10] = function() { return i };
而i的作用域是整个for的花括号内,因此这些函数返回的是同一个i,遍历完后产生了这11个结果,此时我们再调用函数的时候,i已经为10了,因此无论调用哪个函数,结果都是遍历完后的i。
this
var obj = {
foo: function () {
console.log(this);
}
}
var bar = obj.foo;
obj.foo(); //obj
bar(); //window
要理解this,首先要知道函数的三种调用形式
func(p1,p2);
obj.child.method(p1,p2);
func.call(context,p1,p2); //正常形式
context就是this,
上面的例子中,bar()等价于func.call(undefined),这种情况下this为window,
而obj.foo()等价于obj.foo.call(obj),this为obj
有一个特例:
function fn() {
console.log(this);
}
var arr = [fn,fn2];
arr[0]; //这里的this指什么
我们把arr[0]假想为arr.0()
也就相当于arr.0.call(arr)
所以this为arr
对某些框架有其他方法来理解,
如在jQuery,this关键字指向的是当前正在执行事件的元素。
new操作
1.创建一个空对象,并且this变量引用该对象,继承该对象的原型
var obj = {}
2.属性和方法被加入到this引用的对象中
obj.__proto__ = Base.prototype;
3.新创建的对象又this所引用,并且隐式地返回this
Base.call(obj);
在以下例子中
var o = new myObject();
一旦没有加上new,myObject()内部的this将指向全局对象
事件类型
UI事件
1.load事件:页面完全加载后触发
2.unload事件:页面完全卸载后触发
3.resize事件:当浏览器窗口被调整到一个新的高度或宽度时触发
4.scroll事件:文档被滚动期间触发
焦点事件
1.focusout:在失去焦点的元素上触发
2.focusin:在获得焦点的元素上触发
3.blur:在失去焦点的元素上触发
4.DOMFocusOut:在失去焦点的元素上触发
5.focus:在获得焦点的元素上触发
6.DOMFocusIn:在获得焦点的元素上触发
鼠标与滚轮事件
1.click:单击鼠标或按下回车键时触发
2.dbclick:双击鼠标按钮时触发
3.mousedown:按下任意鼠标按钮时触发
4.mouseup:用户释放鼠标按钮时触发
5.mousewheel:通过鼠标滚轮滚动页面时触发
6.mouseenter:鼠标光标首次从元素外部移动到元素范围内时触发(只限于被选元素)
7.mouseover:鼠标指针位于一个元素外部,用户将其首次移入另一个元素边界之内时触发(任何元素,不限于被选元素)
8.mouseleave:位于元素上方的鼠标光标移动到元素范围之外时触发(只限于被选元素)
9.mouseout:鼠标指针位于一个元素的上方,移入到另一个元素时触发(任何元素,不限于被选元素)
10.mousemove:鼠标指针在元素内部移动时重复地触发
键盘与文本事件
1.keydown:按下键盘的任意键时触发
2.keypress:按下键盘的字符键时触发
3.keyup:释放键盘上的键时触发
除此之外,还有
变动事件
H5事件
触摸与手势事件
DOM事件处理程序
DOM0级事件处理程序
var btn = document.getElementById("myBtn");
btn.onclick = function () {
alert("clicked");
}
DOM2级事件处理程序
var btn = document.getElementById("myBtn");
btn,addElementListener("click",function(){
alert("clicked");
},false);
//true: 在捕获阶段调用事件处理程序
//false: 在冒泡阶段调用事件处理程序
跨浏览器的事件处理程序(封装)
var EventUtil = {
addHandler: function(element,type,hander) {
if (element.addEventListener) {
element.addEventListener(type,handler,false);
} else if (element.attachEvent) { //IE
element.attachEvent("on"+type,handler);
} else {
element["on"+type] = handler; //HTML事件
}
}
}
事件委托的原理以及优缺点
function delegateEvent (interfaceEle,selector,type,fn) {
if (interfaceEle.addEventListener) {
interfaceEle.addEventListener(type,eventfn);
} else { //IE
interfaceEle.attachEvent("on"+type,eventfn);
}
function eventfn (e) {
var e = e || window.event;
var target = e.target || e.srcElement; //兼容fireFox
if (matchSelector(target,selector)) {
if (fn) {
fn,call(target,e); //将fn内部的this指向target
}
}
}
}
var odiv = document.getElementById("iDiv");
delegateEvent(odiv,'click',function(){
alert("clicked");
});
优点:
1.大量减少内存占用,减少事件注册
2.新增子对象无需再次对其绑定事件
原生Javascript实现事件代理
<ul id="parentList">
<li id="post-1">1</li>
<li id="post-2">2</li>
</ul>
<script>
function delegateEvent(interfaceEle,type,fn) {
if(interfaceEle.addEventListener) {
interfaceEle.addEventListener(type,fn);
} else { //IE
interfaceEle.attachEvent("on"+type,fn);
}
}
var parentList = document.getElementById("parentList");
delegateEvent(parentList,"click",function(e){
var e = e || window.event; //兼容IE
var target = e.target || e.srcElement; //兼容fireFox
alert(target.id);
});
</script>
这段代码监听了整个<ul>标签并且启用了事件代理,点击<li>标签的时候会返回具体的li标签的id,对新增的对象元素也同样有效
事件冒泡和事件捕获
事件冒泡:事件开始时由最具体的元素接收,然后逐级向上,传播到较为不具体的节点
事件捕获:不太具体的节点更早地接收到事件,而最具体的节点最后接收到事件
DOM事件流:同时支持两种事件模型:捕获型事件和冒泡型事件,但是,捕获型事件先发生。两种事件流会触及DOM中的所有对象,从document对象开始,也在document对象结束。
支持W3C标准的浏览器在添加事件时用addEventListener(event,fn,useCapture)方法,基中第3个参数useCapture是一个Boolean值,用来设置事件是在事件捕获时执行,还是事件冒泡时执行。而不兼容W3C的浏览器(IE)用attachEvent()方法,此方法没有相关设置,不过IE的事件模型默认是在事件冒泡时执行的,也就是在useCapture等于false的时候执行,所以把在处理事件时把useCapture设置为false是比较安全,也实现兼容浏览器的效果。
JSON
json的全称为JavaScript Object Notation,即对象表示法,是一种约定的数据格式。
AJAX
ajax的全称为Asynchronous JavaScript and XML,即异步的JavaScript和XML,
理解它的过程很简单,就是用JS发起一个请求,并得到服务器返回的内容,原生JS写法如下:
//第1步 声明一个对象
var request;
//第2步 根据浏览器赋予对象http请求的方法
if (window.XMLHttpRequest) { //code for IE7+, Firefox, Chrome, Opera, Safari
request = new XMLHttpRequest();
} else { //code for IE6, IE5
request = new ActiveXObject("Microsoft.XMLHTTP");
}
//第3步 确定服务器返回的方法
request.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
console.log(request.responseText);
}
}
//第4步 在之前声明的基础上发起请求
request.open("GET","filename",true);
request.send();
注意以下要点:
IE6及以下版本用的是ActiveXObject方法,其余用XMLHttpRequest方法。
XMLHttpRequest对象三个重要的属性:
onreadystagechange 存储函数(或函数名),每当 readyState 属性改变时,就会调用该函数。
readyState 存有 XMLHttpRequest 的状态。从 0 到 4 发生变化。
0: 请求未初始化
1: 服务器连接已建立
2: 请求已接收
3: 请求处理中
4: 请求已完成,且响应已就绪
status
200:"ok"
404:未找到页面
XMLHttpRequest对象的open()和send()方法:
open(method,url,async);
//规定请求的类型、URL 以及是否异步处理请求。
//method:请求的类型;GET 或 POST
//url:文件在服务器上的位置
//async:true(异步)或 false(同步)
send(string);
//将请求发送到服务器。
//string:仅用于 POST 请求
与 POST 相比,GET 更简单也更快,并且在大部分情况下都能用。
然而,在以下情况中,请使用 POST 请求:
1.无法使用缓存文件(更新服务器上的文件或数据库)
2.向服务器发送大量数据(POST 没有数据量限制)
3.发送包含未知字符的用户输入时,POST 比 GET 更稳定也更可靠
jQuery封装了AJAX的方法,只需要一行代码:
$.get('filename').then(function(response){
//do something
});
原型继承与扩展
Child.prototype = new Parent();
把父类对象赋值给子类构造函数的原型,这样子类就可以访问到父类以及父类的原型,这就叫原型继承。
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function() {
alert ("hello, I'm" + this.name);
};
var BillGates = new Person("Bill Gates"); //建立对象
BillGates.sayHello(); //hello, I'm Bill Gates
Person.prototype.Retire = function() {
alert("poor"+this.name+"byebye!");
} //建立对象后再动态扩展
BillGates.Retire(); //poor BillGates byebye!
这种建立对象后再动态扩展的情况,就叫做原型扩展,新方法仍然可被之前建立的对象调用
js延迟加载
defer和async是动态创建dom的两种方式,defer是延迟加载,async是异步下载脚本
在其他情况下,浏览器会按照script元素在页面中出现的顺序依次进行解析
封装与模块化开发
使代码规范好用,使用简单化,只需要知道参数和返回值,可以转化编程思维角度
var modulel = (function(){
var _count = 0;
var m1 = function(){
//...
}
var m2 = function(){
//...
}
return {
m1:m1,
m2:m2
};
})();
跨域问题的解决方法
1.jsonp(jQuery的$.getScript方法就是利用jsonp跨域解决的)
2.iframe
3.window.name
4.window.postMessage
5.服务器上设置代理页面
DOM操作
(1)创建新节点
createDocumentFragment() //创建一个DOM片段
createElement() //创建一个具体元素
createTextNode() //创建一个文本节点
(2)添加、移除、替换、插入
appendChild()
removeChild()
replaceChild()
insertBefore() //在已有子节点前插入新的节点
(3)查找
getElementsByTagName() //通过标签名称
getElementsByName() //通过元素Name属性值
getElementById() //通过元素id,唯一,所以Element没有s
前端模块化开发
发展历程
1.函数封装
function f1 () {
statement
}
function f2 () {
statement
}
污染了全局变量,容易发生冲突,且模块成员之间没有联系
2.对象的写法
var myModule = {
var1: 1,
var2: 2,
fn1: function () { },
fn2: function () { }
}
调用myModule.fn2(); 避免了变量污染,只需要保证模块名唯一即可
缺陷是外部可以随意修改内部成员:
myModule.var1 = 100;
3.立即执行函数的写法
var myModule = (function(){
var var1 = 1;
var var2 = 2;
function fn1 () { };
function fn2 () { };
return {
fn1: fn1,
fn2: fn2
};
}) ();
console.info(myModule.var1); //underfined
这样就无法修改暴露出来的变量和函数,就是模块化的基础。
模块化的规范 CMD和AMD
commonJS 通用模块定义
1.定义模块
每一个模块都是一个单独的作用域,无法被其他模块读取
2.模块输出
模块只有一个出口,module.exports对象,把模块希望输出的全部内容放入该对象
3.加载模块
加载模块使用require方法,该方法读取一个文件并执行返回文件内部的module.exports对象
例子
//模块定义myModel.js
var name = 'Byron';
function printName () {
console.log(name);
}
function printFullName(firstName) {
console.log(firstName + name);
}
module.exports = {
printName: printName,
printFullName: printFullName
}
//加载模块
var nameModule = require(./myModel.js);
nameModule.printName
AMD 异步模块定义
综合知识
关于缓存的三个关键字
cookie:是储存在浏览器上的一小段数据,用来记录某些当页面关闭或刷新后仍然需要记录的信息。
session:是一种让服务器能够识别某个用户的机制。
localStorage:HTML5本地储存web storage特性的API之一,用于将大量数据保存在浏览器中。
前端性能优化
1.减少http请求次数,JS、CSS源码压缩
2.前端模版,JS+数据,减少DOM操作次数,优化js性能
3.用innerHTML代替DOM操作,减少DOM操作次数,优化js性能
4.设置className,少用内联style
5.少用全局变量,缓存DOM节点查找的结果
6.图片压缩和预加载,将样式表放在顶部,脚本放在底部,加上时间戳
7.避免使用table,显示比div+css布局慢
页面加载过程
1.浏览器根据请求的URL交给DNS域名解析,找到真实IP,向服务器发起请求
2.服务器交给后台处理完成后返回数据,浏览器接收文件(HTML、JS、CSS、图片等)
3.浏览器对加载的资源进行语法解析,建立相应的内部数据结构
4.载入解析到的资源文件,渲染页面,完成
浏览器渲染过程
1.浏览器解析html源码,然后创建一个DOM树,每一个标签都有一个对应的节点,并且每一个文本也有一个对应的文本节点(DOM树的根节点就是documentElement,对应html标签)
2.浏览器解析CSS代码,计算最终的样式数据(对CSS中非法的语法会忽略掉,按优先级排列:浏览器默认设置>用户设置>内联样式>外链样式>html中的style)
3.构建渲染树(忽略不需要渲染的元素)
4.根据渲染树把页面绘制到屏幕上
XHTML
可拓展超文本标记语言,将HTML作为XML的应用而重新定义的标准
其中script需要写成以下形式才能被识别
<script>
//<![CDATA[
//内容
//]]>
</script>
CSS
css定义的权重
div:1
.class:10
#
id:100
内联样式表:1000
#
id div:100+1=101
.class div:10+1=11
.class1 .class2 div:10+10+1=21
权重相同时,最后定义的样式会起作用
盒模型
标准模式:box-sizing:content-box;
怪异模式:box-sizing:border-box;
两种模式的区别:
标准模式会被设置的padding撑开,而怪异模式则相当于将盒子的大小固定好,再将内容装入盒子。盒子的大小并不会被padding所撑开。
.a a a a