[关闭]
@yacent 2016-09-20T17:29:44.000000Z 字数 10836 阅读 920

JavaScript再次复习(秋招)

JavaScript


Chapter1-5 基础

Number对象

原理:先调用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

Chapter6 面向对象的程序设计

创建对象的方式:

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 的完美结合) 【推荐】
    => 每一个实例有自己的数据属性,同时原型上又存在公用的方法,由于调用了构造函数,故实例上的属性会覆盖原型链上的相同属性

Chapter7 函数表达式

函数表达式 VS 函数声明

函数声明提升(hoisting)

递归

递归,最好不要使用函数名来进行递归,那样可维护性不好,当函数突然改了的话,递归可能就不成功了。
因为严格模式下不能使用arguments.callee这个属性不可以使用
解决办法:使用 命名函数表达式
  1. var func = (function f(num) {
  2. if (num == 1 || num == 2) {
  3. return 1;
  4. } else {
  5. return f(num - 1) + f(num - 2);
  6. }
  7. })

闭包

=> 由于内层函数只能取到外层函数作用域链的最后一个值,故在使用需要时时获取到变量值时,要用立即执行函数处理并获取
=> 闭包不要使用过多,内存会爆炸,因为闭包当中使用着外层函数的变量,外层函数无法释放

上下文环境 this

this是动态的,由真实调用者所确定
可以直接看自己博客的总结:https://segmentfault.com/a/1190000006731988?from=timeline&isappinstalled=1

匿名函数的执行环境 具有全局性,即window,所以函数表达式当中的this,通常都是指向window的

Chapter8 BOM

全局变量

全局作用域下的变量都会自动归为 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,故可通过这个判断,是不是第一次打开这个页面

Chapter10 DOM

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

Chapter11 DOM拓展

选择符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来进行检测

Chapter12 DOM2 和 DOM3

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实现精准的控制!!粗细粒度的问题!!

Chapter13 事件

事件流

捕获阶段 (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

Chapter14 表单脚本

表单基础

=> 获取表单
    => 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

Chapter16 HTML5 脚本编程

跨文档消息传递(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()

Chapter20 JSON

语法

=> 双引号 所有键值对都需要,不可以为 单引号
=> 不支持 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()方法

Chapter21 Ajax

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)

Chapter23 离线应用与客户端存储

数据存储

=> 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()

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注