@yacent
2016-09-20T17:29:44.000000Z
字数 10836
阅读 920
JavaScript
原理:先调用valueOf(),再调用toString()
boolean: 1(true) 0(false)
number: 转为数字本身
string: 0('') 数值('1234') NaN('1234a')
null: 0
undefined: NaN
array: 0([]) 数值([1]) NaN([0, 1])
obj: NaN 除非其为null
>> 算术右移 (补符号位)
>>> 逻辑右移 (补0)
! && ||
&& 用于赋值时,返回后者
|| 用于赋值时,若前者为 undefined或者 null,则返回后者
转换为相似的类型
=> 如果比较的两个操作符当中,存在 数值 或者 布尔,则将二者都转换为Number,再进行比较
=> 对象的话,会调用其valueOf(),即Number的本质
去最后一个赋值,其运算相当于赋值
var a = (1, 2, 3, 4); // a = 4
注意,逗号运算符的优先级低于赋值运算符
所以 var a = 1, 2, 3 会导致错误
主要是 传值的方式 不同,前者为按值传递,而后者则为按引用传递
但是,作为函数的参数进行传递时,二者都是 按值传递 的
执行环境 => 确定了函数有权访问的变量和函数
作用域链 => 保证对执行环境有权访问的变量和函数的有序访问
延长作用域链 => with、try-catch
使用 var 声明变量的时候,会将变量添加到最近的作用域(即最近的函数),但是向 if、for这些不是有效的执行环境,最小的是 函数 (function)
创建对象的三种方式:
1. new Object()
2. 字面量的方式
3. Object.create(null,{}) => es5以上才有
使用字面量创建对象时,实际上并不会调用Object的构造函数
Array:
检测数组的三种方法:
1. instanceof
2. isArray() => es5 以上
3. Object.prototype.toString.call() == '[object Array]'
数组的操作方法:
=> toString()、toLocaleString()、join() 默认使用,作为分隔
=> push()、pop()、shift()、unshift()
=> concat()、slice()、splice() 前两个都返回一个新的数组,不影响本数组,最后一个会影响原本的数组
=> sort()、reverse() sort方法排序是为数组每一项都调用toString()方法,故按字典序排序
=> some、every、forEach、filter、map 前两者返回boolean,中间不返回值,最后二者返回数组
=> reduce、reduceRight
Function
创建函数的三种方法:
1. 函数声明
2. 函数表达式
3. new Function() // 最后一个参数 为 函数体,前面为传入函数的参数
函数的内置属性:
arguments、this、caller
属性:
length、prototype
方法:
call、apply、bind(es5以上)
基本包装类型 (Boolean、Number、String)
在调用基本数据类型方法时,会调用基本包装类型,产生实例,但实例值存在于一瞬间
eg: var c += 'ni'
实际过程:
1. var tmp = new String()
2. 调用concat方法
3. tmp = null
创建对象的方式:
1. 工厂模式
=> 缺点:无法识别特定的类型
2. 构造函数模式
=> 缺点:函数无法共享,每一个函数都要创建一个新的实例,浪费内存,何不拿出来,放在原型上呢?
3. 原型模式
=> 优点:
1) 共享属性和方法
2) 减少内存消耗
=> 缺点:
1) 对于引用类型,放在原型上共享,实例之间会相互影响
4. 组合(构造 + 原型)
=> 将基本数据类型放在构造函数当中,将公用方法放到原型上,这样,每一个实例都有自己的基本数据,又可以调用公用方法
tips:
1) 使用对象字面量的方式修改prototype的话,原本constructor会改变,如果很有必要,需要自己重新绑定。其数据数据 emmurable会变为true,可以通过 defineProperty来进行修改
2) 在new了实例之后,不要再通过 对象字面量 的方式修改原型,这样会导致新创建了一个原型,但是又不是实例所指向的原来的那个原型 (即 要修改原型,一定要在实例创建之前)
3) 如果实在是要在实例创建后添加原型的属性,通过 a.prototype.say = function() {..},不要通过字面量的方式
获取key的方式:
1) for ... in ... + hasOwnProperty
2) Object.keys() // es5以上才有该属性,可有1)优雅降级
3) Object.getOwnPropertyNames()
前面两个都是获取可枚举属性,最后一个不论可枚举或者不可枚举,都可以获得
继承:
1. 原型链继承
=> 与使用原型创建对象一样,如果数据为引用类型,则放在原型上,会导致实例之间相互影响
2. 借用构造函数 (super.call(this))
=> 与构造函数创建对象缺点一样,方法无法共享
3. 组合继承 (1 和 2 的完美结合) 【推荐】
=> 每一个实例有自己的数据属性,同时原型上又存在公用的方法,由于调用了构造函数,故实例上的属性会覆盖原型链上的相同属性
函数表达式 VS 函数声明
函数声明提升(hoisting)
递归
递归,最好不要使用函数名来进行递归,那样可维护性不好,当函数突然改了的话,递归可能就不成功了。
因为严格模式下不能使用arguments.callee这个属性不可以使用
解决办法:使用 命名函数表达式
var func = (function f(num) {
if (num == 1 || num == 2) {
return 1;
} else {
return f(num - 1) + f(num - 2);
}
})
闭包
=> 由于内层函数只能取到外层函数作用域链的最后一个值,故在使用需要时时获取到变量值时,要用立即执行函数处理并获取
=> 闭包不要使用过多,内存会爆炸,因为闭包当中使用着外层函数的变量,外层函数无法释放
上下文环境 this
this是动态的,由真实调用者所确定
可以直接看自己博客的总结:https://segmentfault.com/a/1190000006731988?from=timeline&isappinstalled=1
匿名函数的执行环境 具有全局性,即window,所以函数表达式当中的this,通常都是指向window的
全局变量
全局作用域下的变量都会自动归为 window对象的属性
=> 但是与直接在window对象上定义的属性还是有点不同,直接定义的可以通过delete方法删除该属性
窗口关系及框架
每个框架都有一套自己的构造函数,即不同iframe之间的 Array 是不等的
会影响 instanceof 操作符
窗口大小
document.documentElement.clientWidth || document.body.clientWidth
=> 可获取 视口 的大小
在混杂模式下,无法获取 document.documentElemet属性,可以通过 document.compatMode来检测模式 CSS1Compat || BackCompat
间歇调用(setInterval) 和 超时调用(setTimeout)
调用了之后,会返回id ,是此超时调用的唯一标识符
clearTimeout 和 clearInterval 来清除调用
最好是通过 setTimeout 来模拟 setInterval,通过条件判断要不要结束
因为 setInterval会持续不断地调用。那么由于调用机制,有可能上一个还没处理完,下一个调用又开始了。最后会爆炸
location对象
用来 保存文档信息 和 将URL解析为独立的片段
常用字段:
=> protocal
=> host
=> hostname
=> port
=> search
=> hash
=> href
=> pathname
改变窗口的位置:
location.assign() // 调用 location.href = ***,实际上也是调用该函数
改变hash,并不会引起页面的重新加载,可以通过监听hashchange来做相对应的变化
navigator对象
用于识别 客户端浏览器 的标准
=> navigator.userAgent (最常用)
history对象
history.go()
history.back()
history.forward()
history.length // 打开第一个页面时,其长度为0,故可通过这个判断,是不是第一次打开这个页面
Node类型
=> nodeType 属性(检测节点的类型)
常见:ELEMENT_NODE(1) | ATTRIBUTE_NODE(2) | TEXT_NODE(3) | COMMENT_NODE(8)
但是在IE当中 div.nodeType == ELEMENT_NODE 是无效的
所以检测节点类型,最好还是通过数值来进行比较
=> div.nodeType == 1
=> nodeValue | nodeName
=> childNodes
返回nodeList(类数组对象),空白节点也会算进去
=> parentNode
=> 操作节点的方式
=> appendChild() == insertBefore(null, newNode)
=> insertBefore()
=> removeChild()
=> replaceChild(old, new)
=> cloneNode(bool)
=> normalize() // 删除空白文本节点 和 合并相邻的文本节点
Document类型
=> document.documentElement | document.body
=> document.title
=> document.URL (location.href) // 只读
document.domain (location.hostname) // 可读可写
document.referrer // 只读
=> 获取元素
=> document.getElementById()
最好不要有相同的ID名,若有,也会去的第一个
=> document.getElementsByTagName() // 返回 HTMLCollection
可以通过 item 或者 namedItem 来获取返回之后特定的元素
=> document.getElementsByName()
=> document.getElementsByClassName()
=> 动态插入
document.write(),插入script标签的时候,需要将 </script> 转义
Element类型
=> nodeName == tagName
=> div.id | div.className | div.title | div.lang | div.dir
=> 属性操作
=> getAttribute
=> setAttribute
=> removeAttribute
自定义属性,记得在属性名前 使用 data-*,这样可以通过 dataset来访问
添加属性,一定通过setAttribute来添加,除了默认属性之外
=> 元素操作
=> document.createElement()
=> document.createTextNode()
=> document.createDocumentFragment()
由于childNodes会将空白节点算进去,故在使用childNodes获取元素节点时,最好还是先检查一下节点类型,即 nodeType == 1
选择符API(IE8 以上)
querySelector()
querySelectorAll()
可通过 document 或者是 Element 类型进行调用
只接受 合法的CSS选择符
选择符遍历
由于IE9之前,不会将空白节点当做一个节点返回,而IE9及其他浏览器,会将空白节点当做节点返回,故在调用childNodes时候,需要去判断nodeType是否为 1
弥补缺陷,让每次都是返回元素(IE9及以上)
childElementCount
firstElementChild
lastElementChild
nextElementSibling
previousElementSibling
HTML5对于DOM有关的扩展
=> 有关class相关的
=> getElementsByClassName()
=> classList
=> add
=> remove
=> contains
=> toggle
=> HTMLDocument
=> readyState
=> document.compatMode
=> CSS1Compat
=> BackCompat
=> 自定数据属性(dataset)
可以使用 dataset来获取自定义属性(所有含 data-前缀)
!!!该属性仅限于 ff 和 chrome
=> 插入标记
=> innerHTML
=> outerHTML
=> insertAdjacentHTML
=> 性能问题:
使用上面的方法修改DOM的时候,一个很重要的问题,原本DOM上的事件处理函数并不会被删除,这会导致内存泄露的问题,占用过多,所以在使用类似innerHTML的时候,最好是手动清除事件处理函数
=> scrollIntoView (!!this is awesome)
让调用的元素滚动到可视窗口当中!
完美!所有浏览器都支持!
专有拓展
=> <meta http-equiv="x-UA-Compatiable" content="IE=***">
=> children
=> 但在IE8之前,仍然会将注释节点返回
=> contains 用于检测某节点是否存在某后代节点
=> 所有浏览器都支持
=> 手段实现的话,需要不断的去递归parentNode来进行检测
DOM变化
=> (DOM2) 获取CSS属性 document.defaultView.getComputedStyle(ele, null)
(for IE) document.currentStyle()
=> (DOM3) Node类型变化,节点操作方法
=> isSameNode()
=> isEqualNode()
样式
=> 导入样式的方式
=> link
=> <style>属性
=> 内置属性
=> 修改样式
=> div.style.backgroundColor = "red"
=> div.cssText = '....' // 会将原来的属性都重新写一遍
=> 获得最终属性
=> document.defaultView.getComputedStyle(ele, nu)
=> (for IE) document.currentStyle()
=> 元素大小 !!!!!(this is important)
=> 偏移量:
=> offsetWidth、offsetHeight、offsetLeft、offsetTop
=> 每次使用时,都需要重新计算,故这就是为什么在渲染过程中,使用offsetTop、scrollTop、clientWidth等的,都会触发 同步布局
=> 客户区大小
=> clientWidth 、clientHeight
=> 滚动大小
=> scrollWidth、scrollHeight、scrollLeft、scrollTop
=> 元素大小
=> getBoundingClientRect()
=> top、bottom、left、right
遍历(DOM遍历是深度优先遍历) NodeIterator | TreeWalker
=> document.createNodeIterator(root, whatToShow, filter, false)
=> 操作方法:
=> nextNode // 第一次调用,为 根结点,遍历完毕后,再次调用,则为null
=> previousNode
=> document.createTreeWalker(同上)
=> 拥有DOM的所有节点遍历的方法(eg: firstChild)
=> currentNode 可修改起始点,改变遍历的位置
范围 (document.createRange())
=> selectNode()
=> selectNodeContents
=> 可对DOM实现精准的控制!!粗细粒度的问题!!
事件流
捕获阶段 (IE无此阶段)
处于目标阶段
冒泡阶段 (所有浏览器都有,推荐使用冒泡阶段进行事件处理)
事件处理程序
=> DOM0级方法: btn.onclick = function() {...}
=> DOM2级方法:
=> 添加事件的方法
=> addEventListener()
=> removeEventListener()
=> 可添加多个事件,并且会按照添加的顺序来执行
=> (for IE)
=> attachEvent()
=> detachEvent()
=>
1. 可添加多个事件,但执行顺序如 栈,后进先出
2. 使用时,作用域不再是元素本身,而是全局 window
事件对象 (event)
=> 常用的对象属性
event.type
event.target
event.currentTarget
event.preventDefault()
event.stopPropagation()
=> currentTarget === this
currentTarget为绑定事件的对象,而target则是真正作用的目标
=> event.eventPhase
1 表示 捕获
2 表示 处于目标
3 表示 冒泡
=> 兼容问题
=> 在IE当中,并不是所有事件绑定都有event对象,比如在DOM0级的方式,event为window的一个对象。所以较好的获取event对象的方式是 var event = event | window.event
=> event的属性
(for IE)
event.cancelBubble = true (== event.stopPropagation())
event.returnValue = false (== event.preventDefault())
event.srcElement (== event.target)
事件类型 (很多)
=> UI事件: load、ready ...
=> 焦点事件: focus 、 blur (二者都不冒泡)
=> 鼠标:clientX、clientY、pageX、pageY
=> IE无pageX(Y)属性,但是可以通过 scrollLeft + clientX来替代
=> 键盘与文本事件 (keyCode)
=> HTML事件:contextMenu、DOMContentLoaded
=> 触摸事件:touch
=> touchstart、touchmove、touchend
=> 属性:
=> touches:屏幕上的所有手指的集合
=> targetTouches:绑定了touch事件元素上的手指个数
=> changeTouches:屏幕上新增加或者改变的手指的结合
=> https://segmentfault.com/q/1010000002870710
内存和性能
=> 使用事件委托 (利用的是冒泡机制)
=> 移除事件处理程序
=> btn.onclick = null
=> btn.removeEventListener
表单基础
=> 获取表单
=> document.getElementById()
=> document.forms[0]
=> 每个form调用elements属性,可以获取表单子节点 document.forms[0].elements[0]
=> 通用提交按钮
<input type="submit" value="">
=> 会将请求发送前触发 submit 事件 // 如果直接调用 xx.submit()方法,则不会触发submit事件
=> 可以在submit事件当中进行表单验证、过滤等操作
=> 记得 阻止默认事件,即 event.preventDefault()
=> 要避免重复提交,即第一次提交之后,要禁用 btn (可使用表单的 disabled属性)
=> 共有属性及方法
=> 表单共有属性(可读可写):disabled、readOnly、type、name、value(会被提交到服务器的)
=> 共有方法:focus()、blur()
=> 共有事件:focus、blur、change
=> change事件的触发时机是 input 和 textarea失去焦点之后,并且内部改变了内容才会触发
文本框脚本
=> 选择脚本
=> document.forms[0].elements[0].select()
=> 获得焦点的时候,选取全部脚本内容
=> 过滤输入
=> 监听keypress事件,检测charCode,只有keypress事件当中存在charCode
=> eg: /\d/.test(String.fromCharCode(event.charCode) && event.charCode > 9)
=> 自动切换焦点
=> H5约束验证API
=> required、pattern
=> type: email、url、number、range、date
=> novalidate
选择框脚本
=> xx.selected = true
表单序列化
=> serialize 表单的name和value进行编码
=> 编码后形式为 name1=value1&name2=value2...
=> 自己实现:https://github.com/Yacent/FEtraining/blob/master/serialize/serialize.js
跨文档消息传递(XDM)
=> postMessage('...', 'www.origin.com')
=> 第一个参数值接收字符串,如果传入为 对象,则可以使用 JSON.stringify(o)
=> 接收方,监听message事件
原生拖放
1. draggable = true
2. ondragstart => event.dataTransfer.setData('Text', 'asdasd')
3. ondragover => event.preventDefault() => 取消被移入元素默认阻止其他元素进入
4. ondrop => event.preventDefault() => 阻止ff,默认将拖入的东西以接打开
=> event.dataTransfer.getData('Text')
媒体元素 (了解即可)
<video>、<audio>
历史状态管理
=> hashchange + history.pushState
=> pushState({}, title, url) // 状态对象、状态标题、相对URL
=> replaceState()
语法
=> 双引号 所有键值对都需要,不可以为 单引号
=> 不支持 undefined,其余基本数据类型都支持 (调用JSON.stringify() 会自动过滤 )
=> 对象,属性也需要使用双引号
解析与序列化
=> JSON.stringify() -> 将对象序列化为JSON字符串
=> 若对象当中有属性的值为 undefined,则序列化过程中,会忽略该属性
=> 默认去除所有缩进和空格
=> JSON.parse() -> 将JSON字符串转为JS值,对象中字段不带双引号
序列化选项
=> 可接收额外两个参数 array|func、number -> JSON.stringify(obj, filter, space)
=> JSON.stringify(obj, [], 4)
解析选项
=> 可传入一个函数,参数为 key 和 value
=> 函数会对 序列化中个 每个键值对都调用该方法,有点像 array的forEach()方法
XMLHttpRequest对象(XHR) -> IE7以上
=> ajax 过程 (同域限制)
=> 创建实例 new XMLHttpRequest()
=> 调用open -> 启动一个请求
=> 设置回调函数 onreadystatechange
=> 调用 send,发送数据
=> 返回的数据填充到 XHR 对象当中
=> responseText
=> responseXML
=> status
=> statusText
HTTP头部信息
=> 请求头部常用
=> Accepte-Encoding
=> Accepte-Language
=> Host
=> Connection
=> cookie
=> User-Agent
=> Referer
=> 自定义头部
=> setRequestHeader()
=> 必须是在 open 之前调用,否则创建了一个 http请求之后,再设置就没有任何用处了
=> 使用get请求时,若在查询字符串当中传数据,name和value必须使用 encodeURIComponent,不然会发生错误
=> post,必须serialize(), 即表单序列化之后,才能将数据发送出去,不然会出错!
XMLHttpRequest 2级 (解决了 表单序列化的问题)
=> FormData类型 (IE不支持!)
=> var data = new FormData()
=> 方法调用
1. data.append(name, value)
2. 可直接将表单传入 -> new FormData(document.forms[0])
CORS
=> 浏览器默认支持,自动加上 origin字段,服务器通过白名单过滤
=> (for IE) 因为IE默认不支持 CORS,但是他自己有一个 对象是可以实现的,那就是XDR
=> var xdr = new XDomainRequest()
=> xdr.onload = function() {...}
=> xdr.open(method, url) // 都是 异步 的
=> xdr.send(null)
数据存储
=> cookie (4k) -> 大小限制是4k
=> 构成 "name=value;expires=...;domain=...;path=...;secure"
=> name和value是必须的
=> 查看 document.cookie // 会返回该域中电脑上所有cookie
修改 document.cookie = 上面的构成
=> !! 修改的时候,记得 encodeURIComponent()
=> 操作的方法:读取、写入、删除
=> https://github.com/Yacent/FEtraining/blob/master/cookieUtil/cookieUtil.js
ps: 同比一个域下,cookie的数量是有限制的。可以通过子域的方法来管理
web存储机制
=> localStorage
=> sessionStorage
共有方法:
=> setItem()
=> getItem()
=> removeItem()
=> key()
=> clear()