前端知识结构
http: //lingyu.wang/2014/04/30/interview-overview/
http: //codepen.io/skyinlayer/pen/gwuyo
数据类型
原始类型5个(Number, Boolean, String, Null, Undefined)和引用类型Object
引用类型包括Object Function String Array Date RegExp Math Glabal
按值传递
数据类型判断[instanceof constructor 可用于判断自定义类型]
typeof: typeof 判断原始类型
返回一个表达式的数据类型的字符串, 返回包括number, boolean, string, object, undefined,function.
[注意 和基本数据类型不同 返回结果不包括null 多一个function]
语法为typeof(data) 或 typeof data
注意, typeof null也会返回object, 大多数的对象类型( 数组Array、 时间Date等) 也会返回object
constructor: 内部原型属性,可以通过代码重写 [可以判断自定义类型]
判断数组(typeof obj == 'object') && obj.constructor == Array
判断字符(typeof str == 'string') && str.constructor == String;
判断日期(typeof obj == 'object') && obj.constructor == Date
判断函数(typeof obj == 'function') && obj.constructor == Function
判断自定义对象比如有
function A() {}
obj = new A()
(typeof obj == 'object') && obj.constructor == A
PS constructor是prototype中的属性
(new A()).constructor 得到function A() {}
(new A()).constructor 得到function A() {}
instanceof: JavaScript操作符, 会在原型链中的构造器中搜索, 找到则返回true, 否则返回false [可用于判断自定义类型]
语法为 o instanceof A
obj instanceof A 实际上是判断 构造函数是否存在于 某个object的原型链上 所以null instanceof Object 是false
也就是不断的取 obj的原型和A进行比较 直到obj为null
function instance_of(ins, Class) { //ins 表示左表达式,Class 表示右表达式 相当于 ins instanceof Class
var O = Class.prototype; // 取 Class 的显示原型,即构造函数的原型(这里说构造函数是配合上面的英文)
ins = ins.__proto__; // 取 L 的隐式原型
while (true) { //循环查找object的原型链
if (ins === null)
return false;
if (ins === O) // 构造函数的原型存在于 某个object的原型链上
return true;
ins = ins.__proto__; //继续查找
}
}
判断数组还有 Array.isArray(arr) 当然用arr instanceof Array 也是可以的 只不过对于不同框架中的arr对象就判断不了
toString()的方式来判断(可惜不支持自定义类型判断)
Object.prototype.toString.call(arr) === "[object Array]" 不能用arr.toString() 因为被数组重写了
据说主流的库都是用这种方式来判断数组
PS 自定义类型的话调用toString得到 [object Object]
PS 之所以这种方式可以消除不同frame中arr类型的判断 是因为Array实际上是window下的一个属性
window.Array === Array 为true 所以使用instanceof 和 constructor == Array 就判断错误
因为在frame中 window对象不同 所以Array也会不同 即不同frame中的Array 构造函数不同
自动类型转换
http: //www.w3school.com.cn/js/pro_js_typeconversion.asp
3 种主要的原始类型Boolean、Number 和String及其包装后的对象都有 toString() 方法 它返回该对象的字符串表示
强制类型转换
parseInt parseFloat
Boolean(value) - 把给定的值转换成 Boolean 型;
Number(value) - 把给定的值转换成数字( 可以是整数或浮点数);
String(value) - 把给定的值转换成字符串;
Boolean('a') Boolean('1') 当要转换的值是至少有一个字符的字符串、 非 0 数字或对象时, Boolean() 函数将返回 true。 如果该值是空字符串、 数字 0、 undefined 或 null NaN, 它将返回 false。
== 和 ===
== 和 != 操作符会在需要的情况下自动转换数据类型。 但 === 和 !== 不会, 它们会同时比较值和数据类型, 这也使得它们要比 == 和 != 快。
PS == 对于内容相同 但是不同的对象返回的也是false 因此比较两个内容是否相同 往往需要再写一个函数
=== 类型不同的一定不等, 返回false;
两个string严格相等表示它们有相同的字符排列、 相同的长度和每个位置的字符都相同;
两个number严格相等表示它们有相同的数值[ Number(6) === 6 返回true ], NaN和任何东西都不相等, 包括NaN它自己; 正负零彼此之间相等
两个boolean严格相等表示它们同时为true或者同时为false
两个object严格相等唯一的情况是他们引用了相同的object
== 在两边值类型不同的时候, 会做如下的转换再严格比较:
null == undefined 但是 null !== undefined;
和布尔比较 会尽量转换为布尔 和数字比较时, 另一个会尽量转为数字 和字符串比较时, 另一个会尽量转为字符串
如果两个操作数都是对象, 那会比较对象在内存中的引用是否相同。
根据上面的规则, 我们知道: 如果在比较时两个变量的类型很重要, 就要使用严格比较( === );否则可以使用一般比较( == )。
在JavaScript中, 下面的值被当做假( false), 除了下面列出的值, 都被当做真( true):
false
null
undefined
空字符串”
数字 0
NaN
自动转换:发生在运算 求布尔值 和非数值型使用一元运算符+ -的时候 强制转换主要是使用Boolean String Number三个参数的时候
原型链
原型链查找基本概念:
每一个函数 F 都有一个原型对象( prototype) F.prototype
每一个函数都可以通过 new 关键字化身成为一个类构造函数, new F 会产生一个对象 O
在调用对象的某个属性或者方法, 比如 O.xxx 的时候, 会首先查找对象自身是否有这个方法或者属性, 如果没找到就会去对象的构造函数的原型对象中查找( 注意有两个定语), 也就是查找 O 的构造函数 F 的原型对象 F.prototype.xxx
F.prototype 也是一个对象, 查找 F.prototype.xxx 的时候会重复第 3 步的过程】
PS: 任何一个实例对prototype的属性修改后 所有实例都改了
但是如果修改的是this中的属性 则只会在当前实例中体现
所以一般来说 prototype中的属性都是共有的函数
function Animal(name) {
this.name = name;
}
Animal.prototype.getName = function() {
alert(this.name)
}
继承
function Dog() {};
Dog.prototype = new Animal("buddy");
Dog.prototype.constructor = Dog;
var dog = new Dog();
借用构造函数和Prototype形成的组合式继承
function SuperType(name) {
this.name = name;
this.colors = ['red'];
}
SuperType.prototype.sayName = function() {
console.log(this.name);
}
function SubType(name, age) {
SuperType.call(this, name);
this.name = name;
this.colors = ['red'];
}
SuperType.prototype.sayName = function() {
console.log(this.name);
}
function SubType(name, age) {
SuperType.call(this, name);
//继承属性//调用了SuberType这个构造函数 //相当于这里写上了this.name=name;this.colors=xxx
this.age = age;
}
SubType.prototype = new SuperType(); //注意不要写为SubType.prototype=SuperType.prototype 这样的话constructor指向就不对了
this.age = age;
}
SubType.prototype = new SuperType(); //注意不要写为SubType.prototype=SuperType.prototype 这样的话constructor指向就不对了
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function() { //为原型添加函数 这样一来原型就有sayName sayAge两个方法了
console.log(age);
}
SubType.prototype.sayAge = function() { //为原型添加函数 这样一来原型就有sayName sayAge两个方法了
console.log(age);
}
new操作符都做了什么
1、 创建一个空对象, 并且 this 变量引用该对象, 同时还继承了该函数的原型。
2、 属性和方法被加入到 this 引用的对象中。
3、 新创建的对象由 this 所引用, 并且最后隐式的返回 this。
function Base() {
this.xx = 'xx';
}
new Base()的时候
var obj = {}; //创建空对象
obj.__proto__ = Base.prototype; //继承原型
Base.call(obj); //this 引用该对象 所以就有了 obj.xx = 'xx'
作用域链 第三版179
http: //www.cnblogs.com/dolphinX/p/3280876.html
作用域就是变量和函数的可访问范围, 控制着变量和函数的可见性与生命周期, 在JavaScript中变量的作用域有全局作用域和局部作用域。
PS 变量&函数声明提前 JavaScript虽然是解释执行, 但也不是按部就班逐句解释执行的, 在真正解释执行之前, JavaScript解释器会预解析代码, 将变量、 函数声明部分提前解释, 这就意味着我们可以在function声明语句之前调用
function
每个函数都有自己的执行环境, 当函数被调用时, 会创建一个执行环境和以及相应的作用域链。 对于一个内部函数来说,处于第一位的是该函数内的变量,外部函数的活动对象始终处于第二位, 外部函数外的活动对象次之。
一般来说 当函数执行完毕后 局部的活动对象就会被销毁 内存中仅保存全局作用域。
对于闭包来说 当外部函数执行完毕之后 其活动对象也不会被销毁 因为匿名函数的作用域链仍然在引用这个活动对象
直到匿名函数被销毁后 外部函数的活动对象才会被销毁
闭包 第三版179
http: //www.zhihu.com/question/19554716
包裹了一些局部变量的函数叫做闭包
闭包就是由函数创造的一个词法作用域, 里面创建的变量被引用后, 可以在这个词法环境之外自由使用。( https: //secure.wikimedia.org/wikipedia/zh/w/index.php?title=%E9%97%AD%E5%8C%85_%28%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6%29&variant=zh-cn )
闭包的好处:
闭包通常用来创建内部变量, 使得这些变量不能被外部随意修改, 同时又可以通过指定的函数接口来操作。
PS 闭包只能取到包含函数变量的最后一个值(因为是函数 没有被立即执行 可以参考高级程序设计上的例子)
因此循环的事件绑定要采用立即执行函数 因为JS中传参是传递的值
PS 即使是引用类型 传参还是传的值 你可以理解为传递的是一个指针
内存泄露&&垃圾回收
js引擎必须保证当开辟的对象没用的时候(内存引用计数为0),把所分配的内存空间释放出来,这个过程叫做垃圾回收
内存泄漏是指我们已经无法再通过js代码来引用到某个对象,但垃圾回收器却认为这个对象还在被引用,因此在回收的时候不会释放它
this
变量是静态作用域,文法作用域,this是动态的,运行时作用域
this是js的一个关键字, 随着函数使用场合不同, this的值会发生变化。
但是有一个总原则, 那就是this指的是调用函数的那个对象。
this一般情况下: 是全局对象Global。匿名函数也是Global 当作为方法调用, 那么this就是调用了该函数的对象
AJAX Promise Deferred
它的核心思想就是让非同步操作返回一个对象, 其他操作都针对这个对象来完成。 取代回调函数
https: //docs.angularjs.org/api/ng/service/$q
dfd = $q.defer() dfd.resolve(xxx) dfd.rejedct(xxx) return dfd.promise;
return $q.reject(xxx)
数组操作
toString() 返回的是数组中每个值的字符串形式 加上逗号分割组成的新字符串
toLocalString() valueOf() 类似
join() PS join()在不传参数时的表现和toString()一致 相当于 join(',')`
push() pop() 数组末端操作
pop()将返回最后一个元素的同时删去原数组最后一项 push()将返回数组长度
shift() 取走首端的元素并返回该元素
unshift()向首端插入内容并返回操作后的数组长度
PS pop---push 栈 pop---unshift shift---push 队列
reverse() sort()
concat() 可接收多个参数 参数可以是arr 也可以是单个元素
indexOf() lastIndexOf()
slice(start, end) 数组截取 返回[[新]]数组 若index为负数表示从尾部开始数
PS [start, end)
splice() 删除 插入 替换元素 就在原数组上操作
every() 返回Boolean 所有元素都满足条件
filter() 返回满足条件的item组成的[[新]]数组
forEach()
some() 返回Boolean 有元素满足条件
map() 返回对每一项操作过后的item组成的[[新]]数组
reduce() map() 的扩展 前一项和当前item 将返回操作后的一个值
PS 从头开始截取数组可以直接使用length属性(将length赋一个较小的值 则多余项可被删去)
arguments
arguments很像数组 因为可以arg[index] 来取值 但是没有Array.prptotype中的方法
转为数组
Array.prototype.slice.call(arguments); OR [].slice.call(arguments)
slice原理
Array.prototype.slice = function(start, end) {
var result = new Array();
start = start || 0;
end = end || this.length; //this指向调用的对象,当用了call后,能够改变this的指向,也就是指向传进来的对象,这是关键
for (var i = start; i < end; i++) {
result.push(this[i]);
}
return result;
}
String操作
substr(start,length) substring(start, end) slice(start, end)
在start end都是正数的时候 substring slice的表现是一样的 [start, end)
PS 根据上面slice的原理 end不写的时候表示取到末尾
location === window.location === document.location
属性有 hash host(hostname:port) href search
改变任意location一个属性会引发页面刷新 (hash除外)
Arguments.callee表示函数本身
call和apply
xx.call(obj, pa1, pa2);
xx.apply(obj, arguments);
xx.apply(obj, [pa1, pa2]);
模块化
requireJS
DOM和BOM
节点增删查改
createElement() createTextNode() innerHTML() createDocumentFragment()
appendChild() insertBefore() removeChild() replaceChild()
PS js原生没有insertAfter
cloneNode()
style.property 改变样式
xxx.childNodes 获取所有子元素的列表(包括文本节点W3C标准)
xxx.children 功能同上 不包括文本节点 非标准
Fragement 文档片段 保存将来可能会添加到文档中的节点(其初衷是因为不会引发重绘所以添加的速度会快一点 但是chrome对于添加节点本身优化 使用文档片段反而慢了些)
DOM结构—— 两个节点之间可能存在哪些关系以及如何在节点之间任意移动
当前对象为node
返回父节点 node.parentNode, node.parentElement,
返回所有子节点 node.childNodes( 包含文本节点及标签节点), node.children则不包含文本节点 仅返回标签节点
返回第一个子节点 node.firstChild
返回最后一个子节点 node.lastChild
返回同属上一个子节点 node.nextSibling
返回同属下一个子节点 node.previousSibling
document.documentElement 返回文档的根节点 < html >
document.body < body >
document.activeElement 返回当前文档中被击活的标签节点(ie)
event.fromElement 返回鼠标移出的源节点(ie)
event.toElement 返回鼠标移入的源节点(ie)
event.srcElement 返回激活事件的源节点(ie)
event.target 返回激活事件的源节点(firefox)
getAttribute(name) setAttribute(name, value) removeAttribute(name)
根据表单中得name来获取元素
document.getElementById('formID').elements['input_name_value'];
事件绑定
事件模型
http: //www.cnblogs.com/hustskyking/p/problem-javascript-event.html
INNER OUT都有捕获和冒泡时
OUT捕获 INNER捕获 INNER冒泡 OUT冒泡
(1) window.event
表示当前的事件对象, IE有这个对象, FF没有, FF通过给事件处理函数传递事件对象
(2) 获取事件源
IE用srcElement获取事件源, 而FF用target获取事件源
(3) 添加, 去除事件
IE: element.attachEvent(“onclick”, function) element.detachEvent(“onclick”, function)
FF: element.addEventListener(“click”, function, true) element.removeEventListener(“click”, function, true)
冒泡
在某个元素上触发某类事件, 这个事件会向元素的父级元素传播, 由里到外, 直至它被处理(这里指stopPropagation) 或者它到达了元素层次的最顶层
事件冒泡只是事件处理方式的一种, 一共有两种事件处理的方式 1) 从里到外的冒泡型事件 2) 从外到里的捕获型事件
不是所有时间都能冒泡, 有很多事件不冒泡: blur、 focus、 load、 unload
阻止冒泡不能阻止对象的默认行为
可以使用event.stopPropagation() 来阻止冒泡
event.preventDefault() 来阻止元素事件的默认行为
事件代理
通过target来判断就是是谁触发了该事件
var ul = document.getElementById("ul");
ul.addEventListener("click", function(evt) {
if (evt.target.tagName === 'LI') {
console.log("li " + evt.target.getAttribute("value") + " is clicked");
}
}, false);
PS event.target 是DOM元素 获取id可以通过 event.target.id
XHR
XMLHttpRequest 对象提供了在网页加载后与服务器进行通信的方法。
1 创建XHR对象
2 注册readystatechange事件
3 Open 最后 send
function loadXMLDoc(url) {
xmlhttp = null;
if (window.XMLHttpRequest) { //code for all new browsers
xmlhttp = newXMLHttpRequest();
}
elseif(window.ActiveXObject) { //code for IE5 and IE6
xmlhttp = newActiveXObject("Microsoft.XMLHTTP");
}
if (xmlhttp != null) {
xmlhttp.onreadystatechange = state_Change;
xmlhttp.open("GET", url, true);
xmlhttp.send(null);
} else { }
}
function state_Change() {
if (xmlhttp.readyState == 4) { //4 = "loaded"
if (xmlhttp.status == 200) { //200 = OK
} else { }
}
}
Ajax需要注意的地方
http: //www.cnblogs.com/sanmaospace/archive/2013/06/15/3137180.html
http: //www.cnblogs.com/yingsmirk/archive/2012/04/10/2441193.html
ajax干掉了back按钮, 即对浏览器后退机制的破坏。
性能优化
每次遇到<script>标签,浏览器都会停下来等待代码下载并执行,再继续处理其他部分
每次遇到<script>标签,浏览器都会停下来等待代码下载并执行,再继续处理其他部分
(PS 阻塞页面渲染和资源加载)
将JS文件放在页面底部
将JS文件打包
延迟加载JS
作用域链和原型链越长 访问速度越慢
PS 样式css不会阻碍加载
带宽
使用CDN
压缩js、css,图片优化
HTTP优化
减少转向
减少请求数
缓存
尽早Flush
使用gzip
减少cookie
使用GET
DNS优化
减少域名解析时间
增多域名提高并发
JavaScript
放页面底部
defer/async defer="true" 相当于把script标签放到了页面底部 js会在页面解析完毕后执行
将JS文件放在页面底部
将JS文件打包
延迟加载JS
作用域链和原型链越长 访问速度越慢
PS 样式css不会阻碍加载
带宽
使用CDN
压缩js、css,图片优化
HTTP优化
减少转向
减少请求数
缓存
尽早Flush
使用gzip
减少cookie
使用GET
DNS优化
减少域名解析时间
增多域名提高并发
JavaScript
放页面底部
defer/async defer="true" 相当于把script标签放到了页面底部 js会在页面解析完毕后执行
async="true" 不会因为js而阻塞页面的加载 会在下载完后立刻执行 (若js存在依赖可能报错)
CSS
放页面头部
避免@import
其它
CSS
放页面头部
避免@import
其它
JSONP
http: //lingyu.wang/2014/03/24/js-interview-3/
JSON是一种数据交换格式, 而JSONP则是一种非官方跨域数据交互协议。
Ajax直接请求存在跨域的问题, 但使用拥有src属性的标签( < script > , < img > , < iframe > )能不受跨域影响, 服务端返回的时一段可执行的代码 调用了我本地的函数 数据通过参数传入
简述一下cookie, sessionStorage, localStorage的区别
cookie
标准的客户端浏览器状态保存方式, cookie每次发送请求的时候都会被带回到服务器, 从而保证了服务器可以知道浏览器当前的状态, 但同时也意味着其容量不能太大, 最多不能超过4k
sessionStorage
HTML5提供的两种客户端存储数据的新方法( localStorage、 sessionStorage) 之一, 是针对一个session的数据进行存储, 有时间限制。 sessionStorage数据的存储金特定于某个绘画中, 也就是说数据只会保存到浏览器关闭, 但浏览器如果是刷新或重新打开页面, 数据还是存在的。 每在新标签或者新窗口开启一个新页面, 都会初始化一个新的会话。限制是5MB
localStorage
HTML5提供了两种客户端存储数据的新方法的另一种, 它不是针对session的数据进行存储, 而是用于持久化的本地存储, 除非主动删除数据, 否则数据永远不会过期。 它的存储空间比cookie要大得多, 大多数桌面浏览器会设置每个来源5MB的限制, Android版的限制则是2.5 MB
eval?
它的功能是把对应的字符串解析成JS代码并运行;
应该避免使用eval, 不安全, 非常耗性能( 2 次, 一次解析成js语句, 一次执行)。
"use strict";
去除with语句(Eliminates with)
变量在赋值之前必须声明 防止意外的全局变量
禁止this关键字指向全局对象
构造函数必须有new
状态码码
200 服务器正确接受
301 永久移动 302 暂时移动 304 没有改变 401 未授权 403 禁止 502坏网关 503请求失败
innerHTML outerHTML innerText outerHTML
innerHTML与outerHTML在设置对象的内容时包含的HTML会被解析,而innerText与outerText则不会。
在设置时,innerHTML与innerText仅设置标签内的文本,而outerHTML与outerText设置包括标签在内的文本。
也就是说outerHTML会把整个标签都替换掉 outerText也是同样 只不过整个元素都替换成了文本
isNaN 用于判断是否是数字
offsetTop, offsetLeft, offsetWidth, offsetHeight
scrollTop, scrollLeft, scrollWidth, scrollHeight
clientTop, clientLeft, clientWidth, clientHeight
getComputedStyle() (在IE中为currentStyle)
clientTop, clientLeft, clientWidth, clientHeight
getComputedStyle() (在IE中为currentStyle)
它们会强迫浏览器渲染完成 因此减少这些属性的访问
字符串拼接 Array.join() 比 String.concat() 快
如果请求不改变服务器状态指示返回数据,应该使用GET。GET请求会被缓存,如果多次提取相同的数据会提高性能