jQuery系列(二) -- 样式

概述

jQuery 函数包装的对象是类数组对象,对象里面包含了 DOM 对象的信息,并封装了很多操作方法,可以和访问数组一样的方法来访问。

jQuery 对象 和 DOM 对象

联系

可是使用 $ 符号将 DOM 对象封装成 jQuery 对象 $( div )
可是使用 div = $div[ 0 ] 或者 div = $div.get( 0 ) 将 jQuery 对象转化成 DOM 对象

###区别
DOM 对象的属性和方法有: .style .classList
jQuery 对象的属性和方法有:.css .addClass

说明:传递给 $(DOM) 函数的参数是 DOM Object ,则 jQuery 函数会把这个 DOM Object 给包装成一个新的 jQuery 对象

jQuery 选择器

  1. id 选择器: $( “#id” )
  2. 类选择器:$( “.class” )
  3. 元素选择器: $( “element” )
  4. 全选择器:$( “*” )
  5. 层级选择器

    • 子选择器:$( “parent > child” )

    • 后代选择器:$( “ancestor descendant” )

    • 相邻兄弟选择器:$( “prev + next” )
      选择所有紧跟在 prev 元素后的 next 元素

    • 一般兄弟选择器:$( “prev ~ siblings” )
      匹配 prev 元素之后的所有兄弟元素,具有相同的父元素,并匹配过滤 siblings 选择器

  6. 基本筛选选择器

    • $( “:first” ) ==> 匹配第一个元素
    • $( “:last” ) ==> 匹配最后一个元素
    • $( “:not(seletor)” ) ==> 选择所有元素去除不匹配给定的选择器元素
    • $( “:eq(index)” ) ==> 在匹配的集合中选择 index 的元素(从0开始)
    • $( “:gt(index)” ) ==> 在匹配的集合中选择大于 index 的元素(从0开 始)
    • $( “:lt(index)” ) ==> 在匹配的集合中选择小于 index 的元素(从0开 始)
    • $( “:odd” ) ==> 选择索引值为奇数的元素
    • $( “:even” ) ==> 选择索引值为偶数的元素
    • $( “:header” ) ==> 选择所有标题元素
    • $( “:root” ) ==> 选择该文档的根元素
    • $( “:lang(language)” ) ==> 选择指定语言的所有元素
    • $( “:animated” ) ==> 选择所有正在执行动画的元素
      说明:
      1. :eq()、:lt()、:gt()、:even、:odd 用来筛选他们前面的匹配表达式的集合元素,根据之前匹配的元素进一步筛选
      2. :gt() 是一个段落筛选,从指定索引的下一个开始,:gt(1) 实际是从2开始
      
  7. 内容筛选选择器

    • $( “:parent” ) ==> 选择所有含有子元素或者文本的元素

    • $( “:empty” ) ==> 选择所有没有子元素的元素(包括文本节点)

    • $( “:contains(text)” ) ==> 选择所有包含指定文本的元素

    • $( “:has(selector)” ) ==> 选择元素中至少包含指定选择器的元素
      说明:

      1. :contain 和 :has 都有查找的意思,但 contains 查找包含**制定文本**的元素,has 查找包含**指定元素**的元素
      2. 如果 :contains 匹配的文本包含在元素的子元素中,同样认为是符合条件的
      3. :parent 和 :empty 是相反的,但是两者所涉及的子元素都包括文本节点
      
  8. 可见性筛选选择器

    • $( “:visible” ) ==> 选择所有显示的元素

    • $( “:hidden” ) ==> 选择所有隐藏的元素

      隐藏元素的方式

      1. CSS ==> display: none
      2. width: 0; height: 0;
      3. visibility: hidden;
      4. opacity: 0;
        说明:如果元素占据文档中一定的空间,元素被认为是可见的,可见元素的宽高都大于 0 ,元素的 visibility: hidden;opacity: 0; 被认为是可见的,因为他们仍然占据空间布局。不在文档中的元素被认为是不可见的,如果当他们被插入到文档中,jQuery 没有办法知道他们是否是可见的,因为元素可见性依赖于使用的样式。
  9. 属性筛选选择器

    • $( “[attribute |= ‘value’]” ) ==> 选择指定属性值等于给定字符串以该字符串为前缀(该字符串后跟一个连字符 ‘-‘)的元素

    • $( “[attribute = ‘value’]” ) ==> 选择指定属性具有*包含一个给定的子字符串的元素(选择给定属性是包含某些值的元素)

    • $( “[attribute ~= ‘value’]” ) ==> 选择指定属性用空格分隔的值中包含一个给定值的元素

    • $( “[attribute = ‘value’]” ) ==> 选择指定属性是给定值的元素

    • $( “[attribute != ‘value’]” ) ==> 选择不存在指定属性,或者指定的属性不等于给定值的元素

    • $( “[attribute ^= ‘value’]” ) ==> 选择指定属性是以给定字符串开始的元素

    • $( “[attribute $= ‘value’]” ) ==> 选择指定属性是以给定值结尾的元素

    • $( “[attribute]” ) ==> 选择所有具有指定属性的元素,该属性是任意值

  10. 表单元素选择器

    • $( “:input” ) ==> 选择所有 input textarea select 和 button 的元素

    • $( “:text” ) ==> 匹配所有文本框

    • $( “:password” ) ==> 匹配所有密码框

    • $( “:raido” ) ==> 匹配所有单选按钮

    • $( “:checkbox” ) ==> 匹配所有复选框

    • $( “:submit” ) ==> 匹配所有提交按钮

    • $( “:image” ) ==> 匹配所有图像域

    • $( “:reset” ) ==> 匹配所有重置按钮

    • $( “:button” ) ==> 匹配所有按钮

    • $( “:file” ) ==> 匹配所有文件域

    说明:

    1. 除了 input 筛选选择器,其余每个表单类别选择器都对应一个 input 元素的 type 值,所以其都可以用属性筛选选择器替换
    2. ` $( ":password" ) === $( "[type = password]" ) `
    
  11. 表单对象属性筛选选择器

    • $( “:enabled” ) ==> 选取可用的表单元素

    • $( “:disabled” ) ==> 选取不可用的表单元素

    • $( “:checked” ) ==> 选取被选中的 元素

    • $( “:selected” ) ==> 选取被选中的

    说明:

    1. 选择器适用于复选框和单选框,对于**下拉框**元素使用 **:selected** 选择器
    2. 在使用 :checked 的时候,**最佳实践是使用 input: checked** 
    
  12. 子元素筛选选择器

    • $( “:first-child” ) ==> 选择所有父级元素下的第一个子元素

    • $( “:last-child” ) ==> 选择所有父级元素下的最后一个子元素

    • $( “:only-child” ) ==> 如果某个元素是其父元素下的唯一子元素,那么它就会被选中

    • $( “:nth-child(n)” ) ==> 选择所有父级元素下的第 n 个子元素,n 是索引值,从 1 开始

    • $( “:nth-last-child(n)” ) ==> 选择所有父级元素下的第 n 个子元素,计数从最后一个开始

  13. 特殊选择器

    • this ==> 表示当前的上下文对象是一个 HTML 对象,可以调用 HTML 对象所拥有的属性和方法

    • $(this) ==> 代表上下文是一个 jQuery 的上下文对象,可以调用 jQuery 的方法和属性值

jQuery 属性与样式

  1. .attr()
    获取和设置元素属性(DOM操作)

    • .attr(传入属性名) ==> 获取属性值
    • .attr(属性名, 属性值) ==> 设置属性值
    • .attr(属性名, 函数值) ==> 设置属性的函数值
    • .attr(attribute) ==> 给指定元素设置多个属性值
      .attr({属性名1: “属性值1” , 属性名2: “属性值2”, …})

    说明:Attribute 和 Property

    1. Attribute 属性 ,  Property  特性
    2. Attribute 就是 DOM node 自带的属性(id、class、title),Property 是 DOM 元素作为对象,其附加的内容(nodeName、nodeType、tagName)
    
  2. .removeAttr()
    为匹配的元素集合中的每个元素移除一个属性 attributeName

  3. .html()
    获取或设置匹配元素的 HTML 内容

    • .html() ==> 获取集合中第一个匹配元素的 HTML 内容
    • .html(htmlString) ==> 设置每一个匹配元素的 HTML 内容
    • .html(function(index, oldhtml)) ==> 用来返回设置 HTML 内容的一个函数
      说明:
      .html() 方法内部使用的是 DOM 的 innerHTML 属性来操作的,这个操作是针对整个 HTML 内容(包括文本内容)
      
  4. .text()
    得到匹配元素集合中的每个元素的文本内容的结合,包括他们的后代,或者设置匹配元素集合中每个元素的文本内容为指定的文本内容

    • .text() ==> 获取匹配元素集合中每个元素的合并文本,包括他们的后代
    • .text(textString) ==> 用来设置匹配元素内容的文本
    • .text(function(index, text)) ==> 用来返回设置文本内容的一个函数

      说明:.html() 和 .text()

      1. .html() 处理元素内容,.text() 处理文本内容
      2. html() 只能使用在 HTML 文档中,.text() 在 XML 文档和 HTML 文档都能使用
      3. 若处理对象只有一个子文本节点,则 .html 和 .text() 处理结果相同
      
  5. .val()
    处理表单元素的值

    • .val() ==> 获取匹配元素集合中第一个元素的当前值
    • .val(value) ==> 设置匹配的元素集合中每个元素的值
    • .val(function(){}) ==> 一个用来返回设置值的函数

      说明:.html() 和 .text()

      1. .val() 处理 select 元素,当没有选择项被选中时,它将返回 null 
      2. 若 select 元素有 multiple 属性,并且至少一个选择项被选中时, .val() 将返回一个数组,这个数组包含每个选中选择项的值
      3. .val() 方法多用来设置表单的字段的值
      
  6. .addClass()
    动态增加 class 类名

    • .addClass(className) ==> 为每个匹配元素增加一个或多个类名
    • .addClass(function(index, currentClass){}) ==> 这个函数返回一个或更多用空格隔开的要增加的样式名

      说明:

      1. .addClass() 方法不会替换一个样式类名,它只是简单的添加一个类名到元素上
      2. 
      
      1
      2
      3
      4
      5
      6
      7
      <script>
      $('div').addClass(function(index, className){
      if( className.indexOf(index) !== -1 ){
      $(this).addClass('red')
      }
      })
      </script>
  7. .removeClass()
    删除全部或者指定的 class

    • .removeClass([className]) ==> 每个匹配的元素移除一个或对个用空格隔开的样式名
    • .removeClass(function(index, class)) ==> 返回一个或多个要被移除的样式名的函数

      说明:如果 .removeClass() 没有参数,则会移除所有的样式类

  8. .toggleClass()
    在匹配的元素集合中的每个元素上添加或删除一个或多个样式类,取决于这个样式是否存在,即:如果存在就删除一个类,如果不存在就添加一个类

    • .toggleClass(className) ==> 切换类
    • .toggleClass(className,switch) ==> 一个布尔值,用来判断样式是否应该被添加或移除
    • .toggleClass(function(index, class, switch){})
  9. .css()
    获取元素样式属性的计算值或者设置元素的 CSS 属性
    获取

    • .css( “propertyName” ) ==> 获取匹配元素集合中的第一个元素的样式属性的计算值
    • .css( “propertyNames” ) ==> 传递一组数组,返回一个对象结果

      设置

    • .css( ‘propertyName’, ‘value’ ) ==> 设置 CSS
    • .css( ‘propertyName’, function ) ==> 可以传入一个回调函数,返回取到对应的值进行处理
    • .css( ‘properties ) ==> 可以传入一个对象,同时设置多个样式,用 { } 括起

      说明:

      1. 通过 .css() 方法设置的样式属性优先级要高于 .addClass() 方法
      2. 若是静态结构,确定了布局规则,使用 .addClass() 方法增加类
      3. 若是动态的 HTML 结构,在不确定布局或者经常要变化的情况下,使用 .css() 方法
      4. .addClass() 不能获取到指定样式的属性值,.css() 可以获取到指定的样式值

jQuery系列(一) -- touch jQuery

封装函数

尽管 DOM 提供了 API ,可是当我们使用 DOM API 的时候却不尽人意,它提供的功能实在是有限,并且有些方法还没有,所以我们先来封装两个函数。第一个函数作用是获取一个元素节点的所有兄弟,第二个函数作用是可以对一个节点添加或者删除 class 。

获取一个元素节点的所有兄弟

当调用 getSiblings 函数的时候只需要传入一个元素节点即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
function getSiblings(node) {
let allchild = node.parentNode.children
let siblings = {
length: 0
}
for (let i = 0, len = allchild.length; i < len; i++) {
if (allchild[i] !== node) {
siblings[siblings.length] = allchild[i]
siblings.length += 1
}
}
return siblings;
}

操纵一个节点的 class 可以进行添加和删除

调用 operationClass 时,需要传入一个节点和一个 classes 对象operationClass( node, { className: true, className: false } )

1
2
3
4
5
6
function operationClass(node,classes){
for(let key in classes){
let method = classes[key] ? 'add' : 'remove'
node.classList[method](key)
}
}

命名空间

利用命名空间,从而对上述两个函数进行优化
调用方法:
DOM.operationClass.call(undefined,node,{className:true,className:false})
console.log(DOM.getSiblings.call(undefined,node))

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
let DOM = {}
DOM.operationClass = (node,classes) => {
for(let key in classes){
let method = classes[key] ? 'add' : 'remove'
node.classList[method](key)
}
}
DOM.getSiblings = (node) => {
let allchild = node.parentNode.children

let siblings = {
length: 0
}

for (let i = 0, len = allchild.length; i < len; i++) {
if (allchild[i] !== node) {
siblings[siblings.length] = allchild[i]
siblings.length += 1
}
}
return siblings;
}

最终优化

方案一

在 Node.prototype 上添加方法,调用方法是:
node.operationClass.call(node,{className:true,className:false})
node.getSiblings.call(node)
缺点:改变了 Node.prototype

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Node.prototype.operationClass = function(classes){
for(let key in classes){
let method = classes[key] ? 'add' : 'remove'
this.classList[method](key)
}
}

Node.prototype.getSiblings = function(){
let allchild = this.parentNode.children
let siblings = {
length: 0
}

for (let i = 0, len = allchild.length; i < len; i++) {
if (allchild[i] !== this) {
siblings[siblings.length] = allchild[i]
siblings.length += 1
}
}
return siblings;
}

方案二

创造一个新的接口,使用全局变量 otherNode ,无侵入式调用。
调用方法:

let node = otherNode.call(undefined,node)
node.operationClass.call(undefined,{red:true,blue:false})
node.getSiblings.call(undefined))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
window.otherNode = (node) => {
return {
operationClass: (classes) => {
for (let key in classes) {
let method = classes[key] ? 'add' : 'remove'
node.classList[method](key)
}
},
getSiblings : () => {
let allchild = node.parentNode.children
let siblings = {
length: 0
}

for (let i = 0, len = allchild.length; i < len; i++) {
if (allchild[i] !== node) {
siblings[siblings.length] = allchild[i]
siblings.length += 1
}
}
return siblings;
}
}
}
方案二优化

优化方向:

  • 允许用户传入一个 CSS 选择器
  • 添加了操作文本的功能
    调用方法:
    1
    2
    3
    let node = otherNode.call(undefined,'#id')
    node.getSiblings.call(undefined)
    node.operationClass.call(undefined,{red:true,blue:false})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
window.otherNode = (nodeOrSelector) => {
let node
if(typeof nodeOrSelector === 'string'){
node = document.querySelector(nodeOrSelector)
}else{
node = nodeOrSelector
}
return {
operationClass: (classes) => {
for (let key in classes) {
let method = classes[key] ? 'add' : 'remove'
node.classList[method](key)
}
},
setText:(text) => {
node.textContent = text;
},
getSiblings : () => {
let allchild = node.parentNode.children
let siblings = {
length: 0
}

for (let i = 0, len = allchild.length; i < len; i++) {
if (allchild[i] !== node) {
siblings[siblings.length] = allchild[i]
siblings.length += 1
}
}
return siblings;
}
}
}
方案二再优化

优化方向:

  • 满足更多的 CSS 选择器,例如 ul > li
  • 简化了 text 功能,不传参数,就是获取,传参数就是设置
    使用方法:
    1
    2
    3
    4
    5
    6
    7
    let node = otherNode.call(undefined, 'ul > li')
    node.text()
    node.text('hello world')
    node.operationClass.call(undefined, {
    className: false,
    className: true
    })
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
window.otherNode = (nodeOrSelector) => {
let nodes = {}
if (typeof nodeOrSelector === 'string') {
let temp = document.querySelectorAll(nodeOrSelector)
for (let i = 0, len = temp.length; i < len; i++) {
nodes[i] = temp[i]
}
nodes.length = temp.length
} else if (nodeOrSelector instanceof Node) {
nodes = {
0: nodeOrSelector,
length: 1
}
}
nodes.operationClass = (classes) => {
for (let key in classes) {
let method = classes[key] ? 'add' : 'remove'
for (let i = 0, len = nodes.length; i < len; i++) {
nodes[i].classList[method](key)
}
}
},
nodes.text = (text) => {
if (text === undefined) {
let texts = []
for (let i = 0, len = nodes.length; i < len; i++) {
texts.push(nodes[i].textContent)
}
return texts
} else {
for (let i = 0, len = nodes.length; i < len; i++) {
nodes[i].textContent = text;
}
}
}
return nodes
}

jQuery

jQuery 就是将 otherNode 更名为 jQuery,当然 jQuery 的兼容性更好、功能更加丰富(DOM 操作、动画、AJAX操作等等),并且它使用了 prototype 。

JS系列 -- Function 浅析

概述

函数是一个可以执行代码的对象。每个函数都是 Function 类型的实例,并且都与其他引用类型一样具有属性和方法。

Function

每个 Function 的实例的 __proto__ 都指向了 Function.prototype(原型),函数原型(Function.prototype)的 __proto__ 指向了 Object.prototype(对象的原型)

定义函数

  1. 函数声明

    1
    2
    3
    function name( param1[ , param2[, ...] ] ){
    return
    }

    如果 return 省略,那么浏览器将自动添加 return undefined
    function 是关键字,它只能用来声明函数,而 var 则可以用来声明7 种数据类型(number、string、boolean、null、undefined、symbol、 object)中的任意一种。

  2. 函数表达式

    1
    2
    3
    var x = function( param1[ , param2[, ...] ] ){
    return
    }

    匿名函数需要赋给变量,之后才能调用。这个匿名函数称为函数表达式

  3. 混合式

    1
    2
    3
    var fn = function name( param1[ , param2[, ...] ] ){
    return
    }

    混合式中,函数名 name 只在函数体内部有效,指代函数表达式本身,在函数体外部无效。

  4. Function 构造函数
    最后一个参数被始终看成函数体,而前面的参数则枚举了新函数的参数。

    1
    2
    3
    var fn = new Function( 'param1'[ , 'param2'[,' ...'] ] , functionBoby ){
    return
    }

    例子:

    1
    2
    3
    let n = 1;
    let fn = new Function( 'x' , 'y' , 'return x + ' + n + ' + y' );
    fn( 1,2 ) //4,此时的 n 是1
  5. 箭头函数(ES6)

    1
    (param1[ , param2[, ...] ])=> { return  }

形参和实参

  • parameters ==> 形参,定义时的占位符
  • arguments ==> 实参,调用时传入的参数

说明

  • 函数会在执行完 return 语句之后停止并立即退出,因此,位于 return 语句之后的任何代码都永远不会执行。
  • 没有传递值的命名参数将自动被赋值 undefined
  • 所有参数传递的都是值,不可能通过引用传递参数
  • 验证函数,使用 typeof xxx === 'function'

函数声明和函数表达式

  • 函数声明
    浏览器解析器会率先读取函数声明(声明前置),并使其在执行任何代码之前可用
  • 函数表达式
    必须要等到浏览器解析器执行到它所在的代码行,才会被解析执行

声明前置

先声明变量,后声明 function,如果 function 名字和 var 声明名字一样
function 优先。如果赋值就按照赋值的类型。

声明前置

API

自身 API

自身属性 API

  • name ==> 返回函数实例的名称

fn.name

  • length ==> 指明函数的形参个数

Function.prototype API

Function.prototype API

  • call() ==> 调用一个函数, 其具有一个指定的this值和分别地提供的参数(参数的列表)
    作用:

    1. 改变 this 值
      改变 this 值
    2. 操作参数

      语法:

      1
      fun.call(this, arg1, arg2, ...)

      参数:

    3. this :函数运行时的指定的 this 值。

      • 非严格模式下,若 this 为 null 或者 undefined ,则 this 自动指向全局对象(global[ window ]),同时值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的自动包装对象
        非严格模式

      • 严格模式下,若 this 为 undefined ,则 this === undefined
        严格模式
        此处 call 的时候指明了 this === 1,例子不明显,应该 fn.call(),此时便可以打印出 undefined

    4. arguments :伪数组,元素为函数的参数

      • 函数中的参数在内部是用一个伪数组(arguments)来表示,在函数内部通过 arguments 对象来访问参数从而获取传递给函数的每一个参数。
      • arguments 可以被用作被调用对象的所有未指定的参数。可以使用arguments来把所有的参数传递给被调用对象。
      • arguments 对象的长度是由传入的参数个数决定的,不是由定义函数时的命名参数的个数决定的。
  • apply() ==> 调用一个函数, 其具有一个指定的this值,以及作为一个数组(或类似数组的对象)提供的参数。
    作用:和 call() 一样

    1. 改变 this 值
    2. 操作参数

      语法:

      1
      fun.apply(this, arguments)

      使用实例:

    3. 实例1:不关心 this ,所以将 this 传为 null
      apply 实例1

    4. 实例2:关心 this ,this === arr1
      Apply 实例2

    说明:call()apply() 的区别
    apply()call() 作用相同,不同之处在于提供参数的方式
    apply() 使用参数数组,可以将数组里的元素拆分传入参数
    call() 使用一组参数列表,一个一个传递参数
    apply可以使用数组字面量,也可以使用 arguments对 象, arguments 是一个函数的局部变量。

  • bind() ==> 创建一个新的函数, 当被调用时,将其 this 关键字设置为提供的值,返回新创建的函数。在调用新函数时,在任何提供之前提供一个给定的参数序列。
    作用:

    1. 切换上下文(this)
    2. 科里化

      语法:

      1
      fun.bind( this [ , arg1 [ , arg2 [ , ... ] ] ] )

      使用实例:

    3. 实例1:基本用法
      bind 基本用法
    4. 实例2:实际应用
      bind 实际应用
    5. 实例3:利用 this + apply 实现 bind
      重构 bind

new

  1. 创建一个新对象 obj ,对象 __proto__ 指向构造函数的 prototype

    1
    对象. __proto__ === 构造函数.prototype
  2. 调用 call()/apply()/bind()

Call Stack(调用栈)

栈是一种数据结构,特点是先进后出,js 代码执行时都遵循 Call Stack 的规则。如果压栈太多会导致错误(Stack Overflow[ 栈溢出 ])

作用域 和 作用域链

scope(范围、视野、眼界)

如果在函数作用域中没有写 var 直接写 a = 3

1. 优先赋值
2. 沿着作用域链寻找 a 的声明
3. 若作用域链中没有 a 的声明,则声明全局变量并赋值

变量提升

将作用域链中首先要做变量提升

例子1:
1
2
3
4
5
6
var a = 1;
function fn(){
console.log( a );
var a = 2;
}
fn.call(); // a === undefined
例子2:
1
2
3
4
5
6
7
8
9
10
var a = 1;
function fn1(){
console.log( a ); // a === undefined
var a = 2;
fn2.call();
}
function fn2(){
console.log( a ); // a === 1
}
fn1.call();
例子3:
1
2
3
4
5
6
7
8
var obj = { name: ' obj ' }
function fn1(){
function fn2(){
console.log( this ); // this === window
}
fn2.call();
}
fn1.call( obj );
例子4:
1
2
3
4
5
6
var liTags = document.querySelectAll( ' li ' );
for( let i = 0, len = liTags.length; i < len; i++ ){
liTags[ i ].onclick = function(){
console.log( i ) // 打印出的 i 都是最后的数字
}
}

this 值

this 的调用主要有两种方式,一种是函数(function),另一种是作为对象的方法(methods)

判断 this 值就看函数怎么被调用,之后转化为 call 形式

例子1:作为对象的方法调用
1
2
3
4
5
6
7
8
function fn(){
console.log(this)
}
let obj = {
a: 'a',
fn: fn
}
obj.fn() // this === obj
例子2:作为对象属性的深层次嵌套
1
2
3
4
5
6
7
8
9
10
11
function fn(){
console.log(this)
}
let obj = {
a: 'a',
wrapper: {
b: 'b',
fn: fn
}
}
obj.wrapper.fn() // this === { b: 'b', fn: fn } === obj.wrapper
例子3:
1
2
3
4
5
6
7
8
9
10
let obj = {
a: 'a',
fn () {
console.log(fn)
}
}
function fncb(cb){
cb()
}
fncb(obj.fn) // this === 全局变量

闭包

如果一个函数使用了它范围外的值,那么这个函数 + 这个变量就叫做闭包。

声明变量

立即调用函数

window下有很多全局属性
window
当定义变量的时候,有可能不小心覆盖了 window 的全局属性,为了避免覆盖 window 上的属性,可以:

  1. 不用全局属性
  2. 用局部变量
  3. 使用立即调用函数,从而使用局部变量

    1
    2
    () => { }.call()
    () => { }()
  4. 但是如果像上述写法,浏览器会报错,所以要这么写:

    1
    2
    3
    4
    5
    6
    7
    (() => { }.call() )
    (() => { } ).call()
    -() => { }.call()
    +() => { }.call()
    !() => { }.call()
    ~() => { }.call()
    { let } // let 作用域在块级作用域中

套路

  1. 如果同一个函数被多次声明,后面的声明就会覆盖前面的声明

  2. 递归:函数调用自身

  3. JavaScript将函数看做一种值,函数只是一个可以执行的值

  4. 函数表达式函数声明同时有的时候
    function

  5. 不能在条件语句中声明函数(变量提升,是的 if 无效)

  6. 函数执行时所在的作用域,是定义时的作用域,而不是调用时所在的作用域。

  7. 参数传值传递,内存中体现原始值的拷贝
    参数传址传递(复杂类型的值),内存中体现原始值的地址,唯一一种不改变参数的是替换掉整个参数,这时不会影响到原始值(添加属性或方法会影响到原始值)

  8. 如果有同名的参数,则取最后出现的那个值

  9. eval命令的作用是,将字符串当作语句执行。eval没有自己的作用域,都在当前作用域内执行,因此可能会修改当前作用域的变量的值,造成安全问题。为了防止这种风险,JavaScript 规定,如果使用严格模式eval内部声明的变量,不会影响到外部作用域

Tween.js

tweenjs 是使用 JavaScript 中的一个简单的补间动画库,支持数字、对象的属性和 CSS 样式属性的赋值。

tweenjs 以平滑的方式修改元素的属性值,需要传递给 tween 要修改的值、动画结束时的最终值和动画花费时间(duration),之后 tween 引擎就可以计算从开始动画点到结束动画点之间值,从而产生平滑的动画效果。

使用

  • tweenjs CDN

    1
    https://cdnjs.cloudflare.com/ajax/libs/tween.js/17.1.1/Tween.min.js
  • tweenjs 下载到本地,密码是 ywoq

  • tweenjs github

  • tweenjs 实例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    var box = document.createElement('div');
    box.style.setProperty('background-color', '#008800');
    box.style.setProperty('width', '100px');
    box.style.setProperty('height', '100px');
    document.body.appendChild(box);

    function animate(time) {
    requestAnimationFrame(animate);
    TWEEN.update(time);
    }
    requestAnimationFrame(animate);

    var coords = { x: 0, y: 0 };
    var tween = new TWEEN.Tween(coords)
    .to({ x: 300, y: 200 }, 1000)
    .easing(TWEEN.Easing.Quadratic.Out)
    .onUpdate(function() {
    box.style.setProperty('transform', 'translate(' + coords.x + 'px, ' + coords.y + 'px)');
    })
    .start();

示例说明

  1. 假设有一个对象 position ,它的坐标为 x 和 y

    1
    var position = { x: 100, y: 0 }
  2. 改变 x 的值从 100 到 200 ,持续时间为 1s

    1
    2
    var tween = new TWEEN.Tween(position)
    tween.to({x: 200}, 1000)
  3. 创建 tween 对象后,激活它,从而让它开始动画

    1
    tween.start();
  4. 为了平滑的动画效果,需要在同一个循环动画中调用 TWEEN.update 方法

    1
    2
    3
    4
    5
    animate();
    function animate(){
    requestAnimationFrame(animate);
    TWEEN.update();
    }

这个动作将会更新所有被激活的 tweens ,在 1s 内 position.x 将变为 200 。

  1. 可以使用 onUpdate 回调函数将结果打印到控制台上
    1
    2
    3
    tween.onUpdate(function(){
    console.log( this.x );
    })

这个函数在每次 tween 被更新时都会被调用

tweenjs 动画

Tween.js 本身不会运行,你需要通过 update 方法明确告诉它什么时候开始运行,推荐在动画主循环中使用该动画,可以调用 requestAnimationFrame 方法来获得良好的图像性能

使用无参数的调用方法,update 将明确当前时间。也可以为 update 方法法明确一个时间。

1
TWEEN.update(100);

update 的时间为 100毫秒 ,可以使用这种方法来明确代码中所有随时间变化的函数。

控制 tween 动画

  • startstop ==> Tween.startTween.stop 分别用来控制 tween 动画的开始和结束
    对于已经结束和没有开始的动画,Tween.stop 方法不起作用。 Tween.start 可以方法接收一个时间参数,若使用了该参数,tween 动画将在延迟该时间数后才开始动画,否则他将立刻开始动画。

  • update ==> 通过 TWEEN.update 方法执行动画的更新

  • chain ==> 制作多个多行,例如一个动画在另一个动画结束后开始,可以通过 chain 来实现

    1
    2
    tweenA.chain(tweenB);  //tweenB 在 tweenA 之后开始动画,故可以制作一个无线循环的动画
    tweenB.chain(tweenA);
  • repeat ==> 制作循环动画,优于 chain,接收一个用于描述循环次数的参数

    1
    2
    tween.repeat(10);
    tween.repeat(infinity);
  • delay ==> 用于控制动画之间的延迟

    1
    2
    tween.delay(1000);
    tween.start()

回调函数

可以在每次 tween 循环周期的指定时间点调用自定义的函数

  • onStart ==> tween 动画开始前的回调函数
  • onStop ==> tween 动画结束后的回调函数
  • onUpdate ==> 在 tween 动画每次更新后执行
  • onComplete ==> 在 tween 动画全部结束后执行
1
2
3
4
5
6
7
8
9
var tween = new TWEEN.Tween({

}).to({

}).onStart(function(){

}).onUpdate(function(){

})

动画运动算法(缓动函数)easing函数

  • Linear ==> 线性匀速运动效果
  • Quadratic ==> 二次方的缓动(t^2)
  • Cubic ==> 三次方的缓动()
  • Quartic ==> 四次方的缓动()
  • Quintic ==> 五次方的缓动
  • Sinusoidal ==> 正弦曲线的缓动()
  • Exponential ==> 指数曲线的缓动()
  • Circular ==> 圆形曲线的缓动()
  • Elastic ==> 指数衰减的正弦曲线缓动()
  • Back ==> 超过范围的三次方的缓动
  • Bounce ==> 指数衰减的反弹缓动

缓动方式(效果)easing类型

  • easeIn(In) ==> 加速,先慢后快
  • easeOut(Out) ==> 减速,先快后慢
  • easeInOut(InOut) ==> 前半段加速,后半段减速

使用公式

1
.easing(TWEEN.Easing.easing函数.easing类型)

tweenjs 源码

控制动画方法

缓动函数

JS系列 -- 基本包装类型

概述

ECMAScript 提供了 3 个特殊的引用类型:Boolean() Number() String() ,以便于操作基本类型值。

创建

当创建基本包装类型时,相对应的变量会有 __proto__ 的属性,此属性是基本包装类型指向 Object 类型的 prototype
基本包装类型

Boolean()

基本包装类型 Boolean

API
  • valueof() ==> 返回基本类型值 truefalse (重写)

  • toString() ==> 返回字符串 'true''false' (重写)

套路:

1
2
3
4
var f1 = false;
var f2 = new Boolean(false);
if(f1){console.log(1)} //不打印
if(f2){console.log(2)} //打印2,因为在调用 f2 的时候,f2 对应的包装类型是对象,所有的对象都是真值。

Number()

基本包装类型 Number

API
  • valueof() ==> 返回对象表示的基本类型的数值 (重写)

  • toString() ==> 返回字符串形式的数值 (重写)

  • toLocaleString() ==> 返回字符串形式的数值 (重写)

  • toFixed() ==> 按照指定的小数位返回数值的字符串表示,参数为小数位数

  • toExponential() ==> 返回以指数表示法表示的数值的字符串形式,参数为指定输出结果中的小数位数

  • toPrecision() ==> 可能返回固定大小格式,也可能返回指数格式;具体规则看哪种格式更加合适,参数为表示数值的所有数字的位数(不包括指数部分)

String()

基本包装类型 String

API
  • length ==> 属性,表示字符串中包含多少个字符

  • valueof() ==> 返回对象所表示的基本字符串值

  • toLocaleString() ==> 返回对象所表示的基本字符串值

  • toString() ==> 返回对象所表示的基本字符串值

  • chatAt() ==> 以单字符字符串的形式返回给定位置的那个字符,参数为指定位置

  • charCodeAt() ==> 以字符编码的形式返回给定位置的那个字符,参数为指定位置

  • concat() ==> 将一个或多个字符串拼接起来,返回拼接得到的新字符串,可以接受任意多个参数(实践中可以使用加号操作符)

  • slice() ==> 返回被操作字符串的一个子字符串,接受一个或者两个参数,第一个参数指定子字符串的开始位置第二个参数(在指定的情况下)表示子字符串的结束位置[ 开始位置,结束位置 )。如果第二参数未指定,一直到字符串的结束为止。如果传入的参数是负值,将传入的负值与字符串的长度相加

  • substr() ==> 返回被操作字符串的一个子字符串,接受一个或者两个参数,第一个参数指定子字符串的开始位置第二个参数(在指定的情况下)表示子字符串的结束位置[ 开始位置,结束位置 )。如果第二参数未指定,一直到字符串的结束为止。如果传入的参数是负值,将负的第一个参数加上字符串的长度,将负的第二个参数转换为0。

  • substring() ==> 返回被操作字符串的一个子字符串,接受一个或者两个参数,第一个参数指定子字符串的开始位置第二个参数(在指定的情况下)表示子字符串返回的个数,[ 开始位置,个数 )。如果第二参数未指定,一直到字符串的结束为止。如果传入的参数是负值,将所有负值转换为0。

  • indexOf() ==> 在一个字符串中从前向后搜索给定的子字符串,返回子字符串的位置,如果没有找到该位置,则返回 -1,接受一个或者两个参数,第一个参数表示指定的字符串,第二个参数表示从字符串中的那个位置开始搜索。

  • lastIndexOf() ==> 在一个字符串中从后向前搜索给定的子字符串,返回子字符串的位置,如果没有找到该位置,则返回 -1,接受一个或者两个参数,第一个参数表示指定的字符串,第二个参数表示从字符串中的那个位置开始搜索。

  • trim() ==> 创建一个字符串的副本,删除前置及后缀的所有空格,然后返回结果。

  • toLowerCase() ==> 字符串大小写转换

  • toLocaleLowerCase() ==> 字符串大小写转换

  • toUpperCase() ==> 字符串大小写转换

  • toLocalUpperCase() ==> 字符串大小写转换

  • match() ==> 在字符串中匹配模式,接受一个参数,正则或者 RegExp 对象。

  • search() ==> 在字符串中从前向后查找模式,返回字符串中第一个匹配项的索引,如果没有找到匹配项,则返回 -1,接受一个参数,正则或者 RegExp 对象。

  • replace() ==> 替换子字符串,接受两个参数,第一个参数可以是一个 RegExp 对象或者一个字符串,第二个参数可以是一个字符串或者一个函数

  • split() ==> 基于指定的分隔符将一个字符串分割成多个字符串,并将结果放在一个数组中,接受一个或两个参数,第一个参数表示分隔符,第二个参数表示数组的长度。

当在读取模式中访问 Boolean Number 和 String 类型时,后台会自动完成

  1. 创建相应的基本类型的一个实例 ( var temp = new 基本包装类型; )
  2. 在实例上调用制定的方法 ( var 变量 = temp.方法; )
  3. 销毁这个实例 ( temp = null; )

基本类型 和 基本包装类型 区别

基本类型 和 基本包装类型 区别在于存储的路径

  • 基本类型存储在 Stack(栈内存)

  • 基本包装类型在 Stack(栈内存) 存储一个地址,之后对应在 Heap(堆内存) 中的一个对象,其中有很多方法,可以进行操作数据。

  • 例子:

    1
    2
    var n1 = 1;
    var n2 = new Number(1);

    两者区别在于:

    • n1 类型是 Number(基本数据类型) ,直接储存在 Stack(栈内存) 中
    • n2 类型是基本包装类型,在 Stack(栈内存) 存储一个地址,之后 对应在 Heap(堆内存) 中的一个对象,其中有很多方法,可以进行操作数据。

引用类型 和 基本包装类型 区别

引用类型 和 基本包装类型 区别在于对象的生存期

  • 使用 new 操作符创建的引用类型的实例,在执行流离开当前作用域之前都一直保存在内存中

  • 自动创建的基本包装类型的对象,只存在与一行代码的执行瞬间,然后立即被销毁

JS系列 -- JavaScript 对象基础 + 遍历 + 原型链

概述

对象就是一组数据和功能的集合。它相当于哈希(键值对),使用对象属性有 [ ] 操作(obj[ 'name' ])和点操作(obj.name

  • 键名 ==> 字符串(ES6 又引入了 Symbol 值也可以作为键值)
  • 键名 ==> 数值 ==(自动转为)==> 字符串
  • 键名 ==> 不符合标识名的条件(比如第一个字符为数字,或者含有空格或运算符),且也不是数字,则必须加上引号,否则会报错

    1
    obj["name"]   // 必须带有 " " ,如果不带 " " JS 首先会找 name 的值,但是 name === undefined ,自然就报错了
  • 键名 ==> 满足标识符(变量名)的规则 ==> obj.name(可以使用点操作符)

  • delete 操作符删除对象属性
    1
    2
    3
    delect obj["name"]
    obj.name // undefined(无value)
    name in obj // false(无key)

对象声明

  1. 1
    var obj1 = {}
  2. 1
    var obj2 = new Object();  //obj1 和 obj2 没有区别,但是 obj1 === obj2 是 false ,因为存储在 Stsck(栈内存) 中的地址不同
  3. 1
    var obj3 = Object.create(Object.prototype)

基本包装类型:Boolean() Number() String()

基本包装类型有属性 __proto__ ,这个属性引用(指向)了在 Heap( 堆内存 ) 的一个地址,这个地址表示基本包装类型对应的 prototype(原型)。基本包装类型有重写属性(表示重写属性从而覆盖 Object 的 prototype

  • Number()
    基本包装类型 Number

  • String()
    基本包装类型 String

  • Boolean()
    基本包装类型 Boolean

遍历对象

for … in … 遍历 自身 + 原型

通过 Objct.prototype.hasOwnProprety 筛选出自身属性

1
2
3
4
5
for( let key in obj ){
if( obj.hasOwnProperty( key ) ){
console.log( obj[ key ] ) // 此时必须使用 [] ,key 表示的是字符串
}
}

Object for...in 遍历

Object.keys() + Object.values() 遍历 自身可枚举属性

Object.keys() + Object.values() 这两种方法的返回值都是一个数组

1
2
3
Object.keys(obj).forEach( ( item ) => {
console.log( item )
})

Object.keys() + Object.values() 遍历

Object.getOwnPropertyNames() 遍历 自身可枚举 + 自身不可枚举

返回由指定对象的所有自身属性的属性名(包括不可枚举但不包括 Symbol 值作为名称的属性)组成的数组

1
2
3
Object.getOwnPropertyNames( obj ).forEach( (item) => {
console.log( obj[ item ] )
})

proto + prototype

实例化对象的 __proto__ 指向构造函数的 prototype,即对象.__proto__ === 函数.prototype

1
2
3
"1".__proto__ === String.prototype
1.__proto__ === Number.prototype
true.__proto__ === Boolean.prototype

测试

1
2
3
4
5
let arr = []
arr.__proto__ === ???
Array.__proto__ === ???
typeof Array.prototype ???
Array.prototype.__proto__ === ???

答案

1
2
3
4
arr.__proto__ === Array.prototype
Array.__proto__ === Function.prototype
typeof Array.prototype // 'Object'
Array.prototype.__proto__ === Object.prototype

对象属性赋值 + 查找

对象属性赋值是在自身属性赋值,不会修改原型链,而查找会在原型链中查找

new 的实现

var 对象 = new 函数

方法一:Object.create()

1
2
3
4
5
6
function _new(){
let constructor = arguments[0]
let obj = Object.create( constructor.prototype )
constructor.apply( obj, [].slice.call( argumnets, 1 ))
return obj
}

方法二:Object.setPrototypeOf()

1
2
3
4
5
6
7
function _new(){
let constructor = arguments[0]
let obj = {}
Object.setPrototypeOf( obj, constructor )
constructor.apply( obj, [].slice.call( argumnets, 1 ))
return obj
}

instanceof

instanceof 是在原型链中查找是否是其实例(instance)

instanceof 实现

1
2
3
4
5
6
7
8
9
10
function _instanceof( instance, fn ){
let i = instance.__proto__
while( i ){
if( i === fn.prototype ){
return true
}
i = i.__proto__
}
return false
}

继承

实现继承主要是要实现原型的私有属性 + 公共属性

私有属性继承

1
call

公共属性继承

1
2
Object.create()
Object.setPrototypeOf()

私有属性 + 公共属性继承

1
new

继承示例

相关知识点

最简单的克隆

1
2
result = JSON.parse( JSON.stringify( obj ) )
result = Object.assign({}, obj)

JS系列 -- JavaScript DOM API

概述

  1. DOM(Document Object Model)文档对象模型,指的是 XML 文档,需要说明的是,HTML 是 XML 的衍生品,如果要操作 HTML 可以使用关于 XML 的文档 DOM。

  2. JS 中的对象继承自 Object ,页面中的对象继承自 Node 函数。
    Model 将页面中的 Document(html、head、body)和内存中的 Object 一一映射。

  3. DOM 就是把文档变成对象的一种模型。

  4. 内存中的 Object 是一个个 node (构造函数),其包括:

    • Element Node(元素节点)
    • Text Node(文本节点)
    • Comment Node(注释节点)
    • Document Node(文档节点)
  5. DOM 功能:页面中的节点通过构造函数构造出对应的对象

    1
    2
    Element.__proto__ === Node.prototype
    Document.__proto__ === Node.prototype

API

Node 接口

Node 派生自Object
Node是一个接口,许多DOM类型从这个接口继承,并允许类似地处理(或测试)这些各种类型。

属性
  • .childNodes ==> 返回包含指定节点的子节点的集合,该集合为即时更新的集合(live collection)

  • .firstChild ==> 只读属性返回树中节点的第一个子节点,如果节点是无子节点,则返回 null

  • .lastChild ==> 只读属性,返回当前节点的最后一个子节点。如果没有子节点,则返回 null

  • .nextSibling ==> 只读属性,返回其父节点的 childNodes 列表中紧跟在其后面的节点,如果指定的节点为最后一个节点,则返回 null可能会获取到文本节点

  • .previouSibling ==> 返回当前节点的前一个兄弟节点,没有则返回null。可能会获取到文本节点

  • .nodeName ==> 返回当前节点的节点名称(svg 标签返回小写字母,其余返回大写字母

  • .nodeValue ==> 返回或设置当前节点的值

  • .nodeType ==> 只读属性,表示的是该节点的类型

    • 元素节点(Node.ELEMENT_NODE) ==> nodeType === 1
    • 文本节点(Node.TEXT_NODE) ==> nodeType === 3
    • 注释节点(Node.COMMENT_NODE) ==> nodeType === 8
    • 文档节点(Node.DOCUMENT_NODE) ==> nodeType === 9
    • 描述文档类型的 DocumentType节点(Node.DOCUMENT_TYPE_NODE) ==> nodeType === 10
    • 一个 DocumentFragment 节点(Node.DOCUMENT_FRAGMENT_NODE) ==> nodeType === 11(使用 DocumentFragment 进行性能优化
  • .parentElement ==> 返回当前节点的父元素节点,如果该元素没有父节点,或者父节点不是一个元素节点.则 返回null

  • .parentNode ==> 返回指定的节点在DOM树中的父节点

  • .innerText ==> 表示一个节点及其后代的“渲染”文本内容的属性

  • .textContent ==> 表示一个节点及其后代的文本内容
    说明:.innerText.textContent 的区别
    • textContent 会获取所有元素的内容,包括 <script><style>元素,然而 innerText 不会
    • innerText意识到样式,并且不会返回隐藏元素的文本,而textContent会
    • 由于 innerText 受 CSS 样式的影响,它会触发重排(reflow),但textContent 不会
方法
  • .appendChild( ); ==> 添加子元素

  • .cloneNode( deep ); ==> 返回调用该方法的节点的一个副本
    参数deep 表示是否采用深度克隆,如果为 true,则该节点的所有后代节点也都会被克隆,如果为 false,则只克隆该节点本身。默认为 false。

  • Node.contains() ==> 返回一个布尔值,表示传入的节点是否为该节点的后代节点。

  • .insertBefore( newDiv, currentDiv ); ==> 在元素前插入元素

  • .removeChild( child ); ==> 移除一个节点(在页面中移除,但是仍在内存中)

  • .replace( newChild, oldChild ); ==> 用指定的节点替换当前节点的一个子节点,并返回被替换掉的节点。

  • .hasChildNodes( ); ==> 返回一个布尔值,表明当前节点是否包含有子节点

  • .isEqualNode( ); ==> 可以判断两个节点是否相等

  • .normalize() ==> 将当前节点和它的后代节点”规范化“(normalized)。在一个”规范化”后的DOM树中,不存在一个空的文本节点,或者两个相邻的文本节点

Document 接口

Document 接口提供了一些在浏览器服务中作为页面内容入口点而加载的一些页面,也就是 DOM 树

属性
  • .body ==> 返回当前文档的 <body> 元素

  • .characterSet ==> 只读属性返回当前文档的字符编码

  • .childElementCount ==> 只读属性,返回一个无符号长整型数字,表示给定元素的子元素数

  • .children ==> 只读属性,返回 一个Node的子elements ,是一个动态更新的 HTMLCollection

  • .documentElement 是一个会返回文档对象(document)的根元素(如HTML文档的 元素)

  • .domain ==> 获取/设置当前文档的原始域部分, 用于同源策略

  • .firstElementChild ==> 只读属性,返回对象的第一个孩子 Element, 如果没有子元素,则为null

    • .lastElementChild ==> 只读属性,返回对象的最后一个孩子Element,如果没有子元素,则返回null
  • .readyState ==> 描述了文档的加载状态

  • .referrer ==> 返回跳转或打开到当前页面的那个页面的URI

  • .onkeypress ==> 用来获取或设置当前元素的keypress事件的事件处理函数

  • .onclick ==> 返回当前元素的 click 事件句柄代码

  • .onmouseover ==> 当指针设备移动到存在监听器的元素或其子元素的时候,mouseover事件就会被触发。(冒泡)

  • .onmouseout ==> 当指针设备移出存在监听器的元素或其子元素的时候,mouseover事件就会被触发。(冒泡)

  • .onmouseenter ==> 当定点设备(通常指鼠标)移动到元素上时就会触发 mouseenter 事件.类似 mouseover ,它们两者之间的差别是 mouseenter不会冒泡

  • .onmouseleave ==> 当定点设备(通常指鼠标)移出元素时就会触发 mouseleave 事件.类似 mouseout ,它们两者之间的差别是mouseleave `不会冒泡

PC端:
  • .onmousedown ==> 用来获取或设置当前元素的mousedown事件的事件处理函数

  • .onmousemove ==> 用来获取或设置当前元素的mousemove事件的事件处理函数。(当用户在当前元素上移动鼠标时会触发mousemove事件.)。mdn上有 1.显示”提示层”时onmousemove的用法. 2.在进行拖拽操作时onmousemove的用法

  • .onmouseup ==> 用来获取或设置当前元素的mouseup事件的事件处理函数

移动端:
  • .ontouchstart ==> 当触点与触控设备表面接触时触发 ontouchstart 事件

  • .ontouchmove ==> 当触点与触控设备表面移动时触发 ontouchstart 事件

  • .ontouchend ==> 当触点离开触控平面时触发touchend事件.
    特性检测:如果设备不支持 touch 事件,那么元素的 ontouchstart 为undefined ,表示未定义。如果设备支持 touch 事件,那么元素的 onstarttouch 为 null ,表示初始化了

  • .onerror ==> 错误(error)事件的一个事件处理程序(event handler)。针对各种目标的不同类型的错误触发了 Error 事件:

    • JavaScript运行时错误(包括语法错误)发生时,window会触发一个ErrorEvent接口的error事件,并执行window.onerror()

      1
      window.onerror = function(message, source, lineno, colno, error) { ... }
    • 当一项资源(如<img><script>加载失败,加载资源的元素会触发一个Event接口的error事件,并执行该元素上的onerror()处理函数。这些error事件不会向上冒泡到window,不过(至少在Firefox中)能被单一的window.addEventListener捕获。

      1
      element.onerror = function(event) { ... }
  • onscroll ==> 当用户滚动某个元素的内容时 scroll 事件将会被触发

  • scrollTo ==> 滚动到文档中的某个坐标

方法
  • .querySelectorAll() ==> 返回与指定的选择器组匹配的文档中的元素列表 (使用深度优先的先序遍历文档的节点)。返回的对象是 NodeList

  • .querySelector() ==> 返回文档中匹配指定的选择器组的第一个元素(使用深度优先先序遍历文档的节点 | 并且通过文档标记中的第一个元素,并按照子节点数量的顺序迭代顺序节点)。 如果没有找到匹配元素,则返回 null,如果找到多个匹配元素,则返回第一个匹配到的元素。CSS 伪类不会返回任何元素 //直接可以传入带有 # 的 href ,也会返回正确的标签。参数是一个字符串,包含一个或是多个 CSS 选择器 ,多个则以逗号分隔

  • .getElementById() ==>

  • .getElementsByName() ==>

  • .getElementsByClassName() ==>

  • .getElementsByTagName() ==>

  • .createElement(); ==> 创建由tagName 指定的HTML元素

  • .createTextNode(); ==> 创建一个新的文本节点

  • .open() ==> 打开一个要写入的文档。

  • .write() ==> 将一个文本字符串写入由 document.open() 打开的一个文档流。
    注意: 因为 document.write 写入文档流,在关闭(已加载)的文档上调用 document.write 会自动调用 document.open这将清除该文档
  • .close() ==> 关闭向当前文档的数据写入

Element 接口

Element(元素)接口是 Document 的一个对象. 这个接口描述了所有相同种类的元素所普遍具有的方法和属性

属性
  • .clientHeight ==> 只读属性,表示元素的内部高度,以像素计。该属性包括内边距,但不包括垂直滚动条(如果有)、边框和外边距

  • .clientWidth ==> 只读属性,表示元素的内部宽度,以像素计。该属性包括内边距,但不包括垂直滚动条(如果有)、边框和外边距

  • .clientLeft ==> 只读属性,表示一个元素的左边框的宽度,以像素表示。不包括左外边距和左内边距

  • .clientTop ==> 只读属性,表示一个元素顶部边框的宽度,以像素表示。不包括顶部外边距或内边距

  • .innerHTML ==> 设置或获取描述元素后代的HTML语法

  • .scrollHeight ==> 只读属性,表示一个元素的内容高度,包括由于溢出导致的视图中不可见内容。包括元素的padding,但不包括元素的border和margin

  • .scrollWidth ==> 只读属性,表示一个元素的内容宽度,包括由于溢出导致的视图中不可见内容。包括元素的padding,但不包括元素的border和margin

  • .scrollLeft ==> 读取或设置元素滚动条到元素左边的距离

  • .scrollTop ==> 读取或设置元素滚动条到元素顶部的距离

  • Element.className ==> 获取并设置指定元素的class属性的值。可以是由空格分隔的多个class属性值

Element.classList:只读属性,返回一个元素的类属性的实时 DOMTokenList集合。
方法:

  • .add() ==> 添加指定的类值。如果这些类已经存在于元素的属性中,那么它们将被忽略。
  • remove() ==> 删除指定的类值。
  • contains() ==> 检查元素的类属性中是否存在指定的类值。
  • item( Number ) ==> 按集合中的索引返回类值。
  • toggle( String ) ==> 当只有一个参数时:切换 class value; 即如果类存在,则删除它并返回false,如果不存在,则添加它并返回true。
    1
    2
    3
    4
    5
    6
    7
    8
      // div是具有class =“foo bar”的<div>元素的对象引用
    div.classList.remove("foo");

    // 如果visible被设置则删除它,否则添加它
    div.classList.toggle("visible");

    //添加或删除多个类
    div.classList.add("foo","bar");
方法
  • .getAttribute() ==> 返回元素上一个指定的属性值。如果指定的属性不存在,则返回 null 或 “” (空字符串)
    注意:

    1. a.href ==> 获取 a 的 href,带有 http://,是被浏览器处理过了
    2. a.getAttribute() ==> 获取 a 的 href ,不带有 http://,用户的写入
  • .setAttribute() ==> 设置指定元素上的一个属性值。如果属性已经存在,则更新该值; 否则将添加一个新的属性用指定的名称和值。

  • .removeAttribute() ==> 从指定的元素中删除一个属性。

HTMLElement 接口

HTMLElement 接口表示所有的 HTML 元素。一些 HTML 元素直接实现了 HTMLElement 接口,其它的间接实现 HTMLElement 接口

属性
  • HTMLElement.offsetHeight ==> 只读属性,它返回该元素的像素高度,包含该元素的垂直内边距(padding)和边框(border)

  • HTMLElement.offsetWidth ==> 只读属性,它返回该元素的像素宽度,包含该元素的垂直内边距(padding)和边框(border)

  • HTMLElement.offsetTop ==> 为只读属性,它返回当前元素相对于其 offsetParent 元素的顶部的距离

  • HTMLElement.offsetLeft ==> 为只读属性,返回当前元素左上角相对于 HTMLElement.offsetParent 节点的左边界偏移的像素值。

  • HTMLElement.offsetParent ==> 只读属性,返回一个指向最近的包含该元素的定位元素。

方法
  • HTMLElement.blur() ==> 移除当前元素所获得的键盘焦点

  • HTMLElement.click ==> 模拟鼠标左键单击一个元素

  • HTMLElement.focus ==> 设置指定元素获取焦点

JS系列 -- JavaScript事件处理程序

事件

事件就是文档或浏览器窗口中发生的一些特定的交互瞬间。

事件流

事件流描述的是从页面中接收事件的顺序

IE的事件流是事件冒泡流,DOM的事件流是事件捕获流

事件冒泡

事件的开始是由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播到较为不具体的节点(文档)

如果你点击div,那么在div上接收到click事件,之后传播到div的父元素,div父元素接收到click事件,再向上传播至div的爷爷元素,div爷爷元素接收到click事件,再向上传播,直到document

事件捕获

不太具体的节点应该更早的接收到事件,而最具体的节点应该最后接收到事件。事件捕获的用意在于在时间到达预定目标之前捕获它

如果你点击div,那么首先在document上接收到click事件,之后向下捕获到div的爷爷元素,div爷爷元素接收到click事件,再向下捕获至div的父元素,div父元素接收到click事件,再向下捕获,直到div

DOM事件流

“DOM2级事件”规定的事件流包括三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段

先捕获在冒泡,DOM树中每个元素都接收两次事件

事件处理程序( event handler )

响应事件的函数,当有事件触发了之后,作出的回应。

HTML事件处理程序

1
2
3
4
5
6
<script>
function showMessage () {
alert("Hello World!");
}
</script>
<input type = 'button' value = 'Click Me' onclick = 'showMessage()' />

注意:此时的onclick = 'showMessage()'中的 ‘showMessage’ 带有 ‘()’,因为onclick = '要执行的代码',一旦用户点击浏览器就eval('要执行的代码')

DOM2级事件处理程序

  1. addEventListener()添加事件处理程序(事件监听)
    参数:
    • 要处理的事件名
    • 事件处理函数
    • 布尔值[可选]:如果是true,则表示在捕获阶段调用事件处理程序,如果是false或者未设置,表示在冒泡阶段调用事件处理程序
  2. removeEventListener()删除事件处理程序(事件监听)
    参数:

    • 要处理的事件名
    • 事件处理函数
    • 布尔值[可选]:如果是true,则表示在捕获阶段调用事件处理程序,如果是false或者未设置,表示在冒泡阶段调用事件处理程序
  3. 通过addEventListener()添加的事件处理程序只能通过removeEventListener()来删除,并且移除时传入的参数与添加处理程序时使用的参数相同,所以这也就意味着通过addEventListener()添加的匿名函数将无法被移除,所以我们一定要给addEventListener()的事件处理函数命名,从而确保我们能够将其移除。
    优点:可以为同一个元素添加多个事件处理程序

IE事件处理程序

  1. attachEvent()添加事件处理程序(事件监听)
    参数:
    • 要处理的事件名
    • 事件处理函数
  2. detachEvent()删除事件处理程序(事件监听)
    参数:

    • 要处理的事件名
    • 事件处理函数
  3. 由于 IE8 及更早的版本只支持冒泡,所以通过attachEvent()添加的事件处理程序都会被添加到冒泡阶段
    注意:在使用attachEvent()方法的情况下,事件处理程序会在全局作用域中运行,此时this === window.

    跨浏览器的事件处理程序

    使用能力检测。并且要保证处理事件的代码能在 DOM 和 IE 下一致地运行,只需要关注冒泡阶段

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    function addHandler ( element,type,handler ) {
    if ( element.addEventListener ){
    element.addEventListener( type,handler,false );
    } else if ( element.attachEvent ){
    element.attachEvent( 'on' + type,handler );
    } else {
    element[ 'on' + type ] = handler;
    }
    }

    function removeHandler ( element,type,handler ) {
    if ( element.removeEventListener ){
    element.removeEventListener( type,handler,false );
    } else if ( element.detachEvent ){
    element.detachEvent( 'on' + type,handler );
    } else {
    element[ 'on' + type ] = null;
    }
    }

注意:当在目标元素上既有捕获阶段调用的事件处理程序,又有冒泡阶段调用的事件处理程序,则按照代码书写顺序依次调用。

1
2
3
4
5
6
7
btn.addEventListener( 'click',function(){
console.log( '捕获阶段' );
},true );

btn.addEventListener( 'click',function(){
console.log( '冒泡阶段' );
},false );

以上代码依次打印出:
捕获阶段
冒泡阶段

1
2
3
4
5
6
7
btn.addEventListener( 'click',function(){
console.log( '冒泡阶段' );
},false );

btn.addEventListener( 'click',function(){
console.log( '捕获阶段' );
},true );

以上代码依次打印出:
冒泡阶段
捕获阶段

JS系列 -- JavaScript实现继承方式

JavaScript实现继承方式

1. new

Student.prototype = new Person();
得到一个 Person 实例,并且这个实例指向构造器的 prototype 属性和调用了构造函数,因为调用的构造函数,而 Student 只是一个类,并没有实例化,只为了继承,调用构造函数创建一个实例,参数的问题就不容易解决。

例如:当 Person() 中有一个 name 属性时,而 Student 上也有 name 属性时,当
Student.prototype = new Person(); Person 上的 name:undefined ,并且 Student 永远不会用到 Person 上的 name 性,如果 Person 上有很多这样的属性情况可想而知。

new 操作符所做的事情(var a = new A())
a.首先生成一个空对象 a = {} ,它是 Object 的实例
b.设置 a 的原型链 a.proto = A.prototype
c.A.call(a), A 的 this 指向 a,并执行 A 的函数体
d.判断 A 的返回值类型,
如果 ‘没有返回值’ 或者 ‘返回值类型为值类型’ 返回 this 即 a (this === a),
如果有返回值,并且返回值类型为引用类型,就返回这个引用类型的对象,替换掉 a

1
2
3
4
5
var A = function(){
return this.__proto__;
}
var a = new A();
console.log(a) //object{}

解释:首先 this === a, 但是 A 返回的是 this.proto (a.proto), this.proto 指向了原型对象 Object() 构造函数创建的 object 对象,它是一个引用类型,它替换掉 a ,所以这里的变量 a 是一个指针,指向它的原型。

console.log(A.prototype === a); //true

此时 A 构造函数的原型和 a 指针(指向 a.proto)是同一个对象

2. Object.create()

var obj = Object.create({ x:1 });

Object.create()是系统内置函数,参数为对象,返回新创建的对象,并且该对象的原型指向参数

创建空对象,并且对象的 proto 指向参数,既继承了属性和方法,本身又有自己的空对象,对于自己添加的属性和方法不会去更改原型上的属性和方法。

3. call() 和 apply()

call() 和 apply() 是为了动态改变 this 而出现的,当一个 Object 没有某个方法时,
但是其他 Object 有,可以借助 call() 和 apply() 用其他对象的方法来操作。

call() 和 apply() 就是更改 this 指向
例:

1
2
3
4
5
6
7
8
function Person(name){
this.name = name;
}
function Student(name,Klass){
Person.call(this,name);
//此时的 Person 的 this 指向了 Student(即 call 里面的 this)
this.klass = klass;
}

call() 和 apply() 的区别:call() 的参数是扁平化参数,apply() 的第二参数放在数组中

例:

1
2
3
4
5
6
7
Math.max.apply(,)
Math.max(1,2,3); // 3
Math.max([1,2,3]); // NaN
Math.apply(null,[1,2,3]); // 3
Math.max.apply(null,[1,2,3]) === Math.max(1,2,3); // true
Math.max.call(null,1,2,3); // 3
Math.max.call(null,1,2,3) === Math.max(1,2,3); // true

4. 实现继承的套路:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function Person(name){
this.name = name;
}

Person.prototype.sayHi = fucntion(){
console.log('hi!I am '+this.name);
}

function Student(name,Klass){
Person.call(this,name); // 使用 call 继承属性
this.klass = klass; // 设置自身的属性
}

// 使用 Object.create() 继承方法
Student.prototype = Object.create(Person.prototype,{
constructor:{
value:Student;
// 还可以指定 writable configurable enumerable
}
});

// 设置自身方法
Student.prototype.learn = function(subject){
console.log(this.name + 'can learn' + subject);
}

Hello World

Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.

Quick Start

Create a new post

1
$ hexo new "My New Post"

More info: Writing

Run server

1
$ hexo server

More info: Server

Generate static files

1
$ hexo generate

More info: Generating

Deploy to remote sites

1
$ hexo deploy

More info: Deployment

创建新博客并上传方法为:

  1. hexo new 第二篇博客
  2. start 第二篇博客.md 即编辑md文件
  3. hexo generate
  4. hexo deploy
  5. end

开通博客方法

  1. 创建一个目录,并使用 git bash 进入
  2. 在 GitHub 上新建一个空 repository,repository 名称是「你的用户名.github.io」(请将你的用户名替换成真正的用户名)
  3. npm install -g hexo-cli,安装 Hexo
  4. hexo init myBlog
  5. cd myBlog
  6. npm i
  7. hexo new 开博大吉
  8. start 开博大吉.md,编辑这个 md 文件
  9. start _config.yml,编辑网站配置
    把第 6 行的 title 改成你想要的名字
    把第 9 行的 author 改成你的大名
    把最后一行的 type 改成 type: git
    在最后一行后面新增一行,左边与 type 平齐,加上一行 ‘repo: 仓库地址’ (请将仓库地址改为「你的用户名.github.io」对应的仓库地址)
    第 4 步的 repo: 后面有个空格,不要眼瞎。
  10. npm install hexo-deployer-git –save,安装 git 部署插件
  11. hexo deploy
  12. 打开 GitHub Pages 功能
  13. 点击预览链接

你现在应该看到了你的博客!

上传博客源代码

注意:你的用户名 .github.io 上保存的只是你的博客,并没有保存「生成博客的程序代码」,你需要再创建一个名为 blog-generator 的空仓库,用来保存 myBlog 里面的「生成博客的程序代码」。

  1. 在 GitHub 创建 blog-generator 空仓库

  2. 使用 SSH 创建

这样一来,你的博客发布在了「你的用户名.github.io」而你的「生成博客的程序代码」发布在了 blog-generator。所有数据万无一失,你就不会因为误删 myBlog 目录而痛哭了

以后每次 hexo deploy 完之后,博客就会更新;

然后你还要要 add / commit / pull / push 一下「生成博客的程序代码」,以防万一

这个 blog-generator 就是用来生成博客的程序,而「你的用户名.github.io」仓库就是你的博客页面

更改博客主题

  1. 主题合集
  2. 找一个喜欢的主题,进入主题的 GitHub 首页,复制它的 SSH 地址或 HTTPS 地址
  3. git clone ‘地址’
  4. ls 复制下载的主题的名称
  5. cd ..
  6. 编辑 _config.yml 文件,第 75 行改为 theme: ‘主题名称’,保存
  7. hexo generate
  8. hexo deploy
  9. end