资讯专栏INFORMATION COLUMN

JavaScript汇总

用户83 / 1802人阅读

摘要:前端笔记基本数据类型种数据类型基本类型复杂类型基本类型与引用类型的区别基本类型基本类型,是按值访问,可以操作保存在变量中的实际值基本类型的值不可变输出输出其中的实际值并未变化,方法是返回了一个新的字符串也不能对基本类型添加属性和方法表明了基

前端笔记/JavaScript 基本 数据类型 6种数据类型

基本类型: Undefined、Null、Boolean、String、Number

复杂类型: Object

基本类型与引用类型的区别 基本类型
基本类型(String、Boolean、Number、null、undefined),是按值访问,可以操作保存在变量中的实际值

基本类型的值不可变

var name = "jobs"
console.log(name.toUpperCase()) // 输出JOBS
console.log(name) // 输出jobs
name.age = 20
name.say = function() {……}
console.log(name.age) // undefined
console.log(name.say) // undefined
// 其中name的实际值并未变化,toUpperCase()方法是返回了一个新的字符串,也不能对基本类型添加属性和方法,表明了基本类型的值不可变

基本类型的比较是值的比较,只有在值相等时才相等

基本类型的变量存放在栈区

var name = "jobs"
var age = 20
栈区
name age
jobs 20
栈区包含了变量的标识符和值

基本类型复制

var a = 10
var b = a
a++
console.log(a, b) // 11 10
// 基本类型在复制操作后变量是相互独立的互不影响
引用类型
就是除了基本类型外的Object类型

引用类型值可以变,可以有属性和方法

var person = {} // 创建个控对象 --引用类型
person.name = "jozo"
person.age = 22
person.sayName = function(){console.log(person.name)} 
person.sayName() // "jozo"
delete person.name // 删除person对象的name属性
person.sayName() // undefined

// 引用类型的值是同时保存在栈内存和堆内存中的对象
栈区内存保存变量的标识符和指向堆内存中该对象的指针也就是该对象在堆内存中的地址
var person1 = {name:"jozo"}
var person2 = {name:"xiaom"}
var person3 = {name:"xiaoq"}
栈区 堆区
person1 堆内存地址1 ——> Object1
person2 堆内存地址2 ——> Object2
person3 堆内存地址3 ——> Object3
引用类型比较是引用的比较
var p1 = {} 
var p2 = {}
console.log(p1 == p2) // false
// 引用类型是按引用访问的,所以是比较它们的堆内存中地址是否相同
对象引用
var a = {} // 新建空对象
var b = a // 赋值后a和b指向同一个空对象
a.name = "jobs"
b.age = 20
console.log(a.name, a.age, b.name, b.age) // jobs 20 jobs 20
// 引用类型的复制是对象保存在栈区的堆内存的地址的复制,两个变量实际上指向同一个对象,因此任何操作都会互相影响
传递参数

所有函数的参数都是按值传递

var p1 = {
    name: "Vian"
}
var setName = function(obj) {
    obj.name = "jobs"
    return obj
}
var res = setName(p1)
console.log(p1.name) // jobs
console.log(res.name) // jobs

// 把p1传入setName()中,obj和p1指向同一个对象,修改obj的name值其实是修改了它们共同指向的对象的name值
var p = {
    name: "alice"
}

var set = function(ot) {
    ot = {}
    ot.name = "tom"
    return ot
}

var re = set(p)
console.log(p.name) // alice
console.log(re.name) // tom

// 在函数内部ot新定义了一个对象,obj引用了一个局部对象,外部p和内部ot指向了不同对象,所以不会影响
引用类型详解
Object类型

new操作符跟随Object构造函数

var person = new Object()
person.name = "alice"

对象字面量表示法

var person = {
    name: "alice",
    "age": 20
}
属性名也能使用字符串  

访问对象属性一般使用点语法,但js中也能使用方括号语法,并且可以使用变量访问属性

  alert(person.name) // alice
  alert(person["name"]) // alice
  var personName = "name"
  alert(person[personName]) // alice
Array类型
数组每一项都可以保存任何类型的数据,而且数组大小可以动态调整,跟随数据的添加而自动增长
创建数组的方法
var arr = new Array() // 使用Array构造函数

var arr = [] // 使用数组字面量表示法
length属性

length总会返回大于0的整数,而且它不是只读属性

当设置length的值小于当前的数组长度时,会从末尾开始删减元素

当设置length为大于当前数组长度时,则会从末尾新增项,新增每一项的值都为undefined

检测数组

对只有一个全局作用域而言,使用instanceof操作符便可以

if (arr instanceof Array) {
    // 对数组的操作
}

// instanceof操作符问题在于,它假定单一的全局执行环境。若网页中包含不同框架,那么实际上会有两个以上不同的全局执行环境,从而存在两个以上不同的Array构造函数

Array.isArray()方法来判断

if (Array.isArray(arr)) {
      // 对数组的操作
  }
数组常用方法

栈方法:

push(): 可接受任意个参数,逐个添加到数组末尾,并返回修改后数组长度

pop(): 从数组末尾移除最后一项,减少length的值,然后返回移除的项

队列方法:

unshift(): 在数组前面添加任意个项,并返回新数组的长度

shift(): 移除数组第一个项,减少length的值,并返回移除的项

排序方法:

reverse(): 反转数组项的顺序

sort(): 默认升序排列,可接收一个比较函数作为参数,比较函数有两个参数,若第一个参数要在第二个参数之前,则返回负数,相等则返回0,若第一个参数要在第二个参数之后,则返回正数

> sort()会调用每项的toString()转型方法,比较字符串,即使是数值也会转型后比较字符串,因此[0, 1, 5, 10, 15]使用这个方法会变成[0, 1, 10, 15, 5]

操作方法

concat(): 可接收数组、等作为参数,将数组合并

var color1 = ["pink", "yellow"]
var color2 = color1.concat("skyblue", ["black", "orange"])
console.log(color1) // pink,yellow
console.log(color2) // pink,yellow,skyblue,black,orange

* `join()`: 可接受一个参数作为分隔符,将数组转换为字符串

* `slice()`: 基于当前数组创建一个新数组,接受一个或者两个参数,起始位置和结束位置,不会影响当前数组。

> 若只有一个参数,则返回从该参数位置开始到末尾所有的项
> 若两个参数则返回起始位置到结束位置之间的项,但不包括结束位置

splice(): 主要作用向数组中插入项,始终返回一个数组,数组为原数组中删除的项,若没有则返回一个空数组,可以有三种用途

- 删除:删除任意数量的项,接受2个参数:要删除的第一项的位置和要删除的个数
- 插入:在指定位置插入任意数量的项,接受3个参数:起始位置、0(要删除的项数、要插入的项),如果要插入多个项,可以再传入第四个,五个,任意个项
- 替换: 可以在任意位置插入任意项,同时删除任意项,接受3个参数:起始位置、要删除的项数、要插入的项

位置方法

indexOf()lastIndexOf()

都接受2个参数:要查找的项和(可选)表示查找起点位置的索引,indexOf()从头开始找,lastIndexOf()从末尾开始找

都返回要查找的项在数组中的位置,没找到则返回-1,并且查找时是使用全等操作符(===)

迭代方法

共有五个迭代方法,每个方法都接受两个参数:要在每一项上运行的函数和(可选的)运行该函数的作用域对象——影响this的值。传入这些方法中的函数接收三个参数:数组项的值、该项在数组中的位置和数组对象本身

every(): 对数组每一项运行给定函数,若该函数对每一项都返回true,则返回true

some(): 对数组每一项运行给定函数,若该函数对任意一项返回true,则返回true

filter(): 对数组每一项运行给定函数,返回该函数会返回true的项组成的数组

map(): 对数组每一项运行给定函数,返回每次函数调用结果组成的数组

forEach(): 对数组每一项运行给定函数,没有返回值

以上方法都不会修改数组中的包含的值
var numbers = [1, 3, 5, 7, 9, 2, 4, 6, 8, 0]

// every
var a1 = numbers.every(function(item, index, array) {
    return item > 2
})
console.log(a1) // false

// some
var a2 = numbers.some(function(item, index, array) {
    return item > 8
})
console.log(a2) // true

// filter
var a3 = numbers.filter(function(item, index, array) {
    return item % 2 === 0
})
console.log(a3) // [2, 4, 6, 8, 0]

// map
var a4 = numbers.map(function(item, index, array) {
    return item * 2
})
console.log(a4) // [2, 6, 10, 14, 18, 4, 8, 12, 16, 0]

// forEach
/*
没有返回值和for循环迭代数组一样
*/

reduce

reduce(function(total, currentValue [,currentIndex]) [,initialValue]): 是一种数组运算,通常用于将数组的所有成员"累积"为一个值。
    - total:必选,初始值或者累积变量
    - currentValue:必选,当前元素
    - currentIndex:可选,当前元素的索引
    - initialValue:可选,传递给函数的初始值
- 基本用法
/**
* reduce(function(total, currentValue, currentIndex) [,initialValue]) 用于数组计算,通常用于将数组累积为一个值
*/

/**
* 参数tmp是累积变量,item为当前数组成员,index为数组下标;每次运行时item会累加到tmp,最后输出tmp
*/
let arr = [1, 2, 3, 4, 5, 6];

let sum = arr.reduce((tmp, item, index) => tmp + item);

console.log(sum); // 21
- map是reduce的特例
/*
 *map是reduce的特例
 累积变量的初始值可以是一个数组,例子中初始值是一个空数组,结果返回一个新的数组,等同于map操作;map操作都可以用reduce,所以map是reduce的特例
 */
let handler = (newArr, x) => {
    newArr.push(x + 1);
  return newArr;
}

let arr2 = arr.reduce(handler, []);
console.log(arr2); // [ 2, 3, 4, 5, 6, 7 ]
数组排序常见方法
    var arr = [1, 10, 3, 8, 5, 9, 4, 6];

/*冒泡排序*/
    function pops(arr) {
        for (var i = 0; i < arr.length; i++) {
            for (var j = 0; j < arr.length - 1 - i; j++) {
                if (arr[j] > arr[j + 1]) {
                    var temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
            }
        }
        return arr;
    }

    console.log(pops(arr));

/*快速排序*/
    function quick(arr) {
        if (arr.length <= 1) {
            return arr;
        }

        var len = Math.floor(arr.length / 2);
        var aj = arr.splice(len, 1);
        var left = [];
        var right = [];

        for (var i = 0; i < arr.length; i++) {
            if (arr[i] < aj) {
                left.push(arr[i]);
            } else {
                right.push(arr[i]);
            }
        }

        return quick(left).concat(aj, quick(right));
    }

    console.log(quick(arr));
Date类型
Error类型

try/catch/finally

try {
    tryCode - 尝试执行代码块
}
catch(err) {
    catchCode - 捕获错误的代码块
} 
finally {
    finallyCode - 无论 try / catch 结果如何都会执行的代码块
}

// 如果使用了try那么catch和finally必须选一个
RegExp类型
概述
通过RegExp类型来支持正则表达式,使用类似Perl的语法来创建正则表达式
 var expression = / pattern / flags;

其中模式(pattern)可以是任何简单或复杂的正则表达式,每个正则表达式可以有一个或者多个标志(flags),用以标明正则表达式的行为,匹配模式有以下三个标志.

g:全局模式,模式将被用于整个字符串而非在发现第一个匹配项时立即停止

i:不区分大小写模式,即在匹配时忽略大小写

m:多行模式,即在到达一行文字末尾时还会继续查找下一行中是否存在与模式匹配的项

实例方法

exec(): 接受一个参数,即要使用模式的字符串

返回包含第一个匹配项的信息的数组,额外附带2个属性index和input,index表示匹配项在字符串中的位置,input表示应用正则表达式的字符串,若没有匹配项返回null

对于exec()而言即使在模式匹配设置了全局标志(g),也只会返回第一个匹配项,在不设置时,在同一字符串上多次调用也只会始终返回第一个匹配项的信息;而设置了,则每次调用都会继续匹配新的项

test():接受一个字符串参数,在模式与该参数匹配的情况下返回true否则返回false

Function 类型
函数其实是对象,每一个函数都是Function的实例,因此函数名实际上也是一个指向函数对象的指针,不会与函数绑定
函数声明与函数表达式

解析器对函数声明和函数表达式有差别对待

解析器会先读取函数声明,并使其在执行任何代码前可用(可以访问);至于函数表达式,则必须等到解析器执行到所在代码行才会真正被解析

// 函数声明
console.log(sum(10, 10)) // 20

function sum(a, b) {
    return a + b
}

// 函数表达式
console.log(add(10, 10)) // 报错

var add = function(a, b) {
    return a + b
}

函数声明,在代码开始执行前,解析器通过一个名为函数声明提升的过程,读取并将函数声明添加到执行环境中,并放到源代码树的顶端,因此可以先调用再定义

函数表达式,函数位于一个初始化语句中,也就是在执行到函数代码前,变量不会保存有对函数的引用

函数内部属性
在函数内部有两个特殊对象arguments和this

arguments是一个类数组对象,保存着函数的所有参数;它还有一个callee属性,它是一个指针,指向拥有这个arguments对象的函数

callee

function factorial(num) {
    if (num <= 1) {
        return 1
    } else {
        return num * arguments.callee(num - 1)
        /* arguments.callee即是factorial函数 */
    }
}

console.log(factorial(10)) // 3628800

caller,es5中定义的函数对象属性,返回一个调用当前函数的引用,如果是由顶层调用的话,则返回null

function a() {
    console.log(arguments.callee.caller) 
      // 因为arguments.callee就是函数a,所以arguments.callee.caller等于a.caller
}

function b() {
    a()
}

a() // null 因为是顶层调用
b() // b函数的源代码

this,this引用的是(this的值)函数据以执行的环境对象,在网页全局作用域中调用函数时候this的引用对象就是window

函数的属性和方法

每个函数都包含两个属性:length和prototype

length: 形参个数

prototype: 原型

每个函数包含两个非继承而来的方法: apply()和call();

作用都是在特定作用域中调用函数,实际上等于设置this的值

apply():接收2个参数,一个是在其中运行的函数的作用域,一个是Array实例或者arguments对象

call():接收2个参数,一个是在其中运行的函数的作用域,其余参数要直接传递给函数,也就是逐一列举

function sum1(a, b) {
    return a + b
}

function sum2(a, b) {
    var s1 = sum1.apply(this, arguments) // arguments对象
    var s2 = sum1.apply(this, [a, b]) // Array实例
    console.log(s1, s2)
}

sum2(10, 10) //20 20

function sum3(a, b) {
    return sum1.call(this, a, b) // 参数逐一传入
}

console.log(sum3(10, 10)) // 20
apply()call(),真正的用武之地并不在于传递参数,而是扩充函数赖以运行的作用域
var color = "blue"
var o = {
    color: "pink"
}

function sayColor() {
    console.log(this.color)
}

/* this指向了全局作用域window */
sayColor() // blue
sayColor(this) // blue
sayColor(window) // blue

/* this指向了o */
sayColor.apply(o) // pink
sayColor.call(o) // pink

bind(): 它会创建一个函数的实例,其this的值会被绑定到传给bind函数的值

var colors = "skyblue"
var c = {
    colors: "orange"
}

function say() {
    console.log(this.colors)
}

var say2 = say.bind(c)
say2() // orange
bind与apply、call不同,不会立刻执行
Boolean类型
暂无
Number类型

toFixed(): 接受一个参数,代表保留几位小数,会四舍五入

var num = 10
console.log(num.toFixed(2)) // 10.00
String类型
string包含length属性,返回字符串长度
字符串方法

字符方法

charAt()charCodeAt():这两个方法都接收一个参数,即基于0的字符的位置.其中charAt()以单字符字符串的形式返回给定位置的字符,而charCodeAt()则是返回编码

var stringTXT = "hellow"
console.log(stringTXT.charAt(3)) // l
console.log(stringTXT.charCodeAt(3)) // 108

字符串操作方法

concat():用于将一个或多个字符串拼接起来

slice(),substr(),substring():这三个方法都返回被操作字符串的子字符串,也都接受一或两个参数,第一个参数指定开始位置,第二个指定结束位置;slice和substring的第二个参数指定的是子字符串的最后一个字符后面的位置,substr的第二个参数则是指定返回字符的个数

var txt = "abcdefg"
console.log(txt.slice(1, 3)) // bc
console.log(txt.substring(1, 3)) // bc
console.log(txt.substr(1, 3)) // bcd

位置方法

indexOf()lastIndexOf():这两个方法接受2个参数,第一个为要查找的字符串,(可选)第二个表示查找起点位置的索引,都是从字符串中寻找指定的字符串,然后返回该字符串的位置,若没有则返回-1,一个从头开始找,一个从末尾开始找

大小写转换

toLowerCase()toUpperCase():全部转换成小写,大写

toLocaleLowerCase()toLocaleUpperCase():全部转换成小写,大写,这是根据特定地区来转换大小写,在不知道代码运行在哪个语言的情况下使用

字符串的模式匹配方法

match():接受一个参数,参数为正则表达式或者RegExp对象,返回一个数组,数组保存着每一个匹配的子字符串

var txt = "abcdefgabcdefgabcdefg"
var reg = /ab/gi
console.log(txt.match(reg)) // ["ab", "ab", "ab"]

search():接受一个参数,参数为正则表达式或者RegExp对象,从头开始匹配,返回第一个匹配项的索引,没有则返回-1

replace():接收2个参数,字符串或者正则表达式或者RegExp对象,第二个则是用于替换的字符串或者函数;若第一个参数为字符串则只会替换第一个子字符串,要想替换所有,则必须是正则表达式,并且指定全局标志(g);若第二个参数是函数,则给这个函数传入的三个参数分别是匹配项第一个匹配项,第二个等等,最后两个参数为匹配项的位置和原始字符串

split():将字符串以指定分隔符分割成字符串,分隔符可以是字符串也可以是正则表达式或者RegExp对象;也可以接受第二个参数,用于指定数组大小

内置单体对象
Global对象
URI编码方法

encodeURI():主要用于整个uri,它不会对本身属于uri的特殊字符进行编码如冒号、正斜杠、问好、井号

encodeURIComponent():主要用于对其中一段进行编码,它会对任何非标准字符进行编码

var uri = "http://www.who.com/search?wd=123"
console.log(encodeURI(uri))
// 结果: http://www.who%08.com/search?wd=123
console.log(encodeURIComponent(uri))
// 结果: http%3A%2F%2Fwww.who%08.com%2Fsearch%3Fwd%3D123
encodeURIComponent使用的比encodeURI多

decodeURI():解码方法,只能对encodeURI()编码的进行解码

decodeURIComponent():解码方法,可以解码所有字符

eval

eval():这个方法像是一个完整的ECMAScript解析器,它接受一个字符串参数,就是要执行的js代码字符串

Math对象
常用属性

Math.PI: π的值

常用方法

求极值

min(),max():求最小,最大值;可以接受任意个参数

舍入方法

ceil():向上求整

floor():向下求整

round():四舍五入

随机值

random(): 返回一个0-1之间的随机数,但不包含0和1,从区间内取一个值,可以用一下公式

(max - min) * Math.random() + min

绝对值

abs(): 绝对值

次方

pow(a, n): 第一个为底,第二个参数为次方值,a的n次方

原型和闭包 对象
(undefined,number,string,boolean)属于简单的值类型,不是对象。函数、数组、对象、null、构造函数都是对象,都是引用类型判断一个变量是不是对象,值类型用typeof,引用类型用instanceof

在js中数组、函数、对象都是对象,对象里面一切都是属性,只有属性,没有方法; 方法也是一种属性

对象都是通过函数创建的,函数是对象

function F() {
  this.name = "jobs"
}

var f1 = new F() // 对象可以通过函数创建
var arr = [1, 2, 3] // 语法糖,本质是下面的简写

var arr = new Array() // Array是构造函数
arr[0] = 1
arr[1] = 2
arr[2] = 3
原型
prototype原型

函数默认有一个prototype属性,prototype属性的值是一个对象(属性的集合),默认只有一个constructor属性指向这个函数本身

[image:DF9C0DF3-83B1-4F0C-8F2B-39C112BA24F6-1188-00000B31D26AD3B2/prototype-1.png]

如图所示,superType是一个函数,右边是原型

原型作为对象,属性的集合,可以自定义许多属性,例如Object,里面包含了其他几个属性

[image:C2050E95-6407-4A59-9B93-D6F7081CD863-1188-00000B3DEE188E44/instanceof-2.png]

可以在自定义的方法的prototype中新增自己的属性

function Fn() {

Fn.prototype.name = "王福鹏"
Fn.prototype.getYear = function() {
    return 1988
}

}

[image:E2D062D3-B7D0-457C-97D2-6607E729A1C5-1188-00000B470B9F9F19/prototype-3.png]

function Fn() {
    Fn.prototype.name = "王福鹏"
    Fn.prototype.getYear = function() {
        return 1988
    }
}

var f1 = new Fn()
console.log(f1.name) // 王福鹏
console.log(f1.getYear()) // 1988
console.log(f1.__proto__ === Fn.prototype) // true

上述代码,f1对象是从Fn中new出来的,这样f1就可以调用Fn中的属性,因为每个对象都有一个隐藏属性"__proto__",这个属性引用了创建这个对象的函数的prototype,所以f1.__proto__ === Fn.prototype,这里的"__proto__"成为隐式原型

隐式原型
__proto__是浏览器提供的属性,就是[[prototype]]这个隐藏属性,__proto__因为不是规范属性,所以要避免使用;

es5中用Object.getPrototypeOf(obj)函数获得一个对象的[[prototype]]

es6中用Object.setPrototypeOf(obj, prototype)函数可以直接修改一个对象的[[prototype]]

参数obj将被设置原型的对象.

prototype该对象新的原型(可以是一个对象或者null).

每个对象都有一个__proto__,可称为隐式原型

__proto__是一个隐藏属性

var obj = {}
console.log(obj.__proto__ === Object.prototype)

有上述代码可知:每个对象都有一个__proto__`属性指向创建该对象的函数的prototype属性

有一个特例Object.prototype.__proto__ === null

instanceof 操作符
console.log(Function instanceof Function) // true
console.log(Function instanceof Object) // true
console.log(Object instanceof Function) // true
console.log(Object instanceof Object) // true

instanceof判断规则是,沿着a的__proto__来找,沿着b的prototype来找,如果引用的同一个引用就返回true

[image:E4783050-568F-4369-A33E-192AE586FBE7-1188-00000B9545C250D1/instanceof-1.png]

即instanceof表示的就是一种继承关系,或者原型链的结构

[image:8826A813-C433-43DB-9C3D-CF0964451DDA-1188-00000B97B94D72D0/instanceof-2.png]

继承

JavaScript的继承是用原型链来体现的

function Foo() {}
var f1 = new Foo()

f1.a = 10

Foo.prototype.a = 100
Foo.prototype.b = 200

console.log(f1.a, f1.b) // 10 200
上述代码,f1是Foo函数new出来的对象,f1.a是f1对象的基本属性,而f1.b是从Foo.prototype得来的,因为f1.__proto__指向Foo.prototype

访问一个对象的属性时,现在基本属性中查找,如果没有,就沿着__proto__链向上查找,这就是原型链

如何区分一个属性是基本的还是从原型中找到的,用hasOwnProperty

[image:802EAC7F-3F88-4EA9-A120-E988C06FB7B3-1188-00000B9C76399666/prototype-4.png]

f1中没有hasOwnProperty方法,它是从Object.prototype而来的

[image:7231EB7A-F0A5-482E-9632-8C88F3FA7749-1188-00000BA5D0FE85B9/prototype-5.png]

对象的原型链都会找到Object.prototype,因此所有对象都有Object.prototype的方法,这就是继承

执行上下文

上下文环境

[image:B0C9A9CF-FB04-4776-A2E5-AA72796DC573-1188-00000BA936740391/this-1.png]

- 第一句报错a未定义,二三句a为undefined,说明在js代码一句句执行前,浏览器就已经开始一  些准备工作,其中就包括对变量的声明,而不是赋值,赋值操作是在运行到语句时才执行;

[image:2185D116-A4C9-41BC-BF30-92F3E57EC3B3-1188-00000BAD8E05AD77/this-2.png]

- 这是第一种情况

[image:DCC314B3-40D8-4F45-8DC1-72AB6A089648-1188-00000BB1A9090280/this-3.png]

- 第一种情况是对变量只声明,未赋值;第二种直接赋值给了this;这也是‘准备工作’之一

[image:88C73D7D-40A0-470F-81BE-B2651D40A767-1188-00000BB70805B00A/this-4.png]

- 在这里对函数表达式和普通变量赋值一样,而函数声明则立刻赋值了

准备工作完成的事

变量、函数表达式 —— 变量声明,默认赋值为undefined

this —— 赋值

函数声明 —— 赋值

这三种数据准备情况就是‘执行上下文’或者‘执行上下文环境’

js在执行代码片段前都会进行准备工作来生成‘执行上下文’,代码片段分三种情况:全局代码、函数体、eval代码

在函数中,arguments和函数参数会直接赋值;函数每调用一次,都会产生一个新的执行上下文环境;而且函数在定义的时候(不是调用的时候),就已经确定函数体内部自由变量的作用域

在执行代码之前,把将要用到的所有的变量都事先拿出来,有的直接赋值了,有的先用undefined占个空

全局代码的上下文环境为

普通变量(包含函数表达式) 声明(默认赋值为undefined)
函数声明 赋值
this 赋值

如果代码片段是函数体,则在此基础上附加

参数 赋值
arguments 赋值
自由变量的取值作用域 赋值
this

函数中this到底是什么值,是在函数真正被调用时确定的,函数定义时确定不了;this永远指向的是最后调用它的对象,也就是看它执行的时候是谁调用的;因为this取值是执行上下文环境的一部分,每次调用都会产生一个新的执行环境上下文

分四种状况

构造函数

  function F(num) {
    this.num = num
    this.log = "txt"
    console.log(this)
  } 
- 把函数作为构造函数,那么this就是new出来的对象
- 函数作为对象的一个属性
-  如果函数作为对象的一个属性,并且作为对象的属性被调用时,this指向该对象
  var obj = {
    x: 10,
    fn: function() {
        console.log(this) // {x:10, fn: function}
        console.log(this.x) // 10
    }
  }
  obj.fn()
- 函数调用call和apply,bind时调用
- 当一个函数被call和apply调用时,this的值就取传入的对象的值
  var obj = {
      x: 10
  }

  function fn() {
      console.log(this)
      console.log(this.x)
  }

  fn.call(obj) // {x: 10} 10
  fn.apply(obj) // {x: 10} 10
  var fns = fn.bind(obj)
  fns() // {x: 10} 10

全局和调用普通函数

this指向window

作用域

作用域

[image:C6407A7F-8B78-4A86-8C15-6697981F687C-1188-00000BFD4FA9B6C0/this-5.png]
[image:08320C13-AE3E-4C60-A615-25442AD209CA-1188-00000BFECEDB8EC1/this-6.png]

作用域中变量的值是在执行过程中产生的确定的

如果要查找一个作用域下某个变量的值,就需要找到这个作用域对应的执行上下文环境,再在其中寻找变量的值

自由变量和作用域链

要到创建这个函数的那个作用域中取值——是“创建”,而不是“调用”,切记切记——其实这就是所谓的“静态作用域”

作用域链

[image:12DFA8C1-973E-4680-A97D-C57F33ABEBCB-1188-00000C050C4391C4/this-7.png]

面向对象的程序设计 属性类型
包含两种数据属性和访问器属性
数据属性
数据属性包含一个数据值的位置,在这个位置可以读取和写入值,数据属性有4个描述其行为的特性

[[Configurable]]:表示能否通过delete删除属性,从而重新定义属性,能否修改属性特性,能否把属性修改为访问器属性

[[Enumerable]]:能否通过for-in循环返回属性

[[Writable]]:能否修改属性值

[[Value]]:包含这个属性的数据值,默认undefined

直接在对象上定义的属性,它们的[[Configurable]]、[[Enumerable]]、[[Writable]]的值默认为true,[[Value]]为指定值
  var p = { name: "nico" } // [[Value]]的值为 nico
要修改属性默认特性,必须使用es5的Object.defineProperty()方法,接收三个参数,属性所在对象,属性名字和一个描述符对象。其中描述符对象属性必须是:configurable、enumerable、writable、value.设置其中一个或多个值(小写)

可以多次调用Object.defineProperty(),但是Configurable一旦设置为false(不可配置),就不能再将其设置为true(可配置),而且为false时其他属性也受到限制了

在调用Object.defineProperty()时,若不设置,configurable、enumerable、writable的值都为false

var Person = {}
Object.defineProperty(Person, "name", {
    configurable: false,
    wirtable: false,
    value: "alice"
})

console.log(Person.name) // alice

delete Person.name

console.log(Person.name) // alice

Person.name = "Nico"

console.log(Person.name) // alice

Object.defineProperty(Person, "name", {
    configurable: true,
    value: "Nico"
})

console.log(Person.name) // 报错
访问器属性
访问器属性不包含数据值,包含一对getter和setter函数(不过它们不是必须的),在读取访问器属性时,会调用getter函数,它负责返回有效的值;在写入访问器属性时会调用setter函数,它负责处理数据;访问器有如下4个特性

* [[Configurable]]:表示能否通过delete删除属性,从而重新定义属性,能否修改属性特性,能否把属性修改为访问器属性

[[Enumerable]]:能否通过for-in循环返回属性

[[Get]]:在读取时调用的函数,默认值为undefined

[[Set]]:在写入属性时调用的函数,默认值为undefined

访问器属性不能直接定义,必须使用Object.defineProperty()来定义
var book = {
     _year: 2014, // _ 是一种常用标记,用于表示只能通过对象方法访问的属性
     edition: 1
 }

 Object.defineProperty(book, "year", {
     get: function() {
         return this._year
     },
     set: function(val) {
         if (val > 2014) {
             this._year = val
             this.edition = val - 2014
         }
     }
 })

 book.year = 2016
 console.log(book.edition) // 2
这是使用访问器属性的常见方式,即设置一个属性的值会导致其他属性发生变化  

不一定要同时使用getter和setter,只指定getter表示属性不能写,尝试写入属性会被忽略;只指定setter表示不能读,否则会返回undefined;在严格模式下都会抛出错误

定义多个属性
Object.defineProperties(),es5中定义的,可以通过描述符一次定义多个属性,接收2个对象参数,第一个是要添加和修改其属性的对象,第二个对象的属性与第一个对象中要添加或修改的属性要一一对应
var book = {}
  Object.defineProperties(book, {
      _year: {
          value: 2000
      },

      edition: {
          writable: true,
          value: 1
      },

      year: {
          get: function() {
              return this._year
          },

          set: function(val) {
              if (val > 2000) {
                  this._year = val
                  this.edition += val - 2000
              }
          }
      }
  })

  console.log(book.year) // 2000
  book.year = 2017
  console.log(book.edition) // 18
读取属性的特性
Object.getOwnPropertyDescriptor(),可以取得给定属性的描述符;接收2个参数:属性所在对象和要读取的其描述符的属性名,返回值为一个对象;如果是数据属性,这个对象的属性有configurable、enumerable、writable、value;如果是访问器属性,这个对象的属性有configurable、enumerbale、get、set
创建对象 工厂模式
 function createperson(name, age) {
      var o = {}
      o.name = name
      o.age = age
      o.say = function() {
          console.log(this.name, this.age)
      }
      return o
  }

  var p1 = createperson("alice", 18)
  var p2 = createperson("lulu", 20)

  console.log(p1 instanceof Object) // true
  console.log(p1 instanceof createperson) // false
工厂模式解决了创建多个相似对象的问题,但没有解决对象识别问题(怎么知道一个对象的类型)
构造函数模式
  function Person(name, age) {
      this.name = name
      this.age = age
      this.say = function() {
          console.log(this.name, this.age)
      }
  }

  var p1 = new Person("alice", 18)
  var p2 = new Person("lulu", 20)

  console.log(p1 instanceof Object) // true
  console.log(p1 instanceof Person) // true
与工厂模式对比,没有显示的创建对象,直接将属性和方法赋给了this对象,没有return语句  

在这个模式下创建的所有对象既是Object的实例又是Person的实例

创建自定义构造函数意味着可以将它的实例标识为一种特定类型,这正是构造函数模式胜过工厂模式的地方

使用构造函数会使得每个方法都要在每个实例上创建一遍,在上述例子中p1和p2都有say方法,但那不是同一个Fuction的实例,因为在js中函数是对象,会导致不同的作用域链和标识符解析

console.log(p1.say == p2.say) // false
原型模式
创建的函数都有一个prototype(原型)属性,它是一个指针,指向一个对象,这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法,也就是prototype就是通过调用构造函数而创建的那个对象实例的原型对象
function Person() {}
Person.prototype.name = "nico"
Person.prototype.say = function() {
    console.log(this.name)
}

var p1 = new Person()
p1.say() // nico
console.log(p1 instanceof Person) // true

理解原型对象

只要创建了新函数,就会为该函数创建一个prototype属性,这个属性指向函数的原型对象

默认情况下,所有原型对象都会自动获得一个constructor(构造函数)属性,这个属性包含一个指向prototype属性所在函数的指针,例如前面的代码中Person.prototype.constructor指向Person;通过这个构造函数可以继续为原型对象添加其它属性和方法

创建了自定义构造函数后,其原型对象默认只会取得constructor属性;至于其他方法,则从Object继承而来

当调用一个构造函数创建一个新的实例后,该实例内部包含一个指针(内部属性),指向构造函数的原型对象;es5中这个指针叫[[Prototype]],在脚本中没有标准的方式访问[[Prototype]],但Firefox,Safari,Chrome在每个对象上都支持一个属性__proto__;这个链接存在于实例与构造函数的原型对象之间,而不是实例与构造函数之间

可以通过对象实例访问保存在原型中的值,但却不能重写原型中的值

原型模式最大问题是由其共享的本性所导致的,改变一个实例的属性,所有实例都会变

更简单的原型语法

function Person() {}
Person.prototype = {
    name: "nico",
    say: function() {
        console.log(this.name)
    }
}

var p1 = new Person()
p1.say() // nico
console.log(p1 instanceof Person) // true
console.log(p1.constructor == Person) // false

以对象字面量形式创建,会导致constructor属性不再指向Person

可以设置constructor为适当值

  function Person() {}
  Person.prototype = {
      constructor: Person,
      name: "nico",
      say: function() {
          console.log(this.name)
      }
  }
  var p1 = new Person()
  p1.say() // nico
  console.log(p1 instanceof Person) // true
  console.log(p1.constructor == Person) // true
但这样会导致constructor的[[Enumerable]]为true,原生的constructor为不可枚举属性,这时候要用es5的Object.defineProperty()重写constructor属性
    function Person() {}
    Person.prototype = {
        constructor: Person,
        name: "nico",
        say: function() {
            console.log(this.name)
        }
    }

    Object.defineProperty(Person.prototype, "constructor", {
        enumerable: false,
        value: Person
    })

    var p1 = new Person()
    p1.say() // nico
    console.log(p1 instanceof Person) // true
    console.log(p1.constructor == Person) // true
组合模式 - 最常用方式

概述

组合使用构造函数模式和原型模式

这是创建自定义类型最常见的模式

构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性

这样,每个实例都有一份自己的实例属性的副本,同时又共享着对方法的引用,还支持向构造函数传参

这种模式是目前使用最广泛的一种创建自定义类型的方法,可以说是用来定义引用类型的一种默认模式

function Person(name, age) {
    this.name = name
    this.age = age
}

Person.prototype = {
    constructor: Person,
    say: function() {
        console.log(this.name, this.age)
    }
}

var p1 = new Person("alice", 20)
var p2 = new Person("nico", 30)
p1.say()
console.info(p1.name == p2.name) // false
console.info(p1.say == p2.say) // true
动态原型模式

为了解决独立的构造函数和原型,动态原型模式将所有信息封装到构造函数中,而通过在构造函数中初始化原型,又保持了同时使用构造函数和原型的优点;也就是通过检测某个应该存在的方法是否有效来决定是否初始化原型

function Person(name, age) {
      this.name = name
      this.age = age
      if (typeof this.say != "function") {
          Person.prototype.say = function() {
              console.log(this.name, this.age)
          }
      }
  }

  var p1 = new Person("alice", 20)
  var p2 = new Person("nico", 30)
  console.info(p1.name == p2.name) // false
  console.info(p1.say == p2.say) // true

这里只有在say方法不存在时,才会将它添加到原型中,这段代码只有在初次调用构造函数时才会执行

if语句检查可以使初始化之后应该存在的任何属性或方法,不必一堆if语句来检测每个属性和每个方法,只要检查其中一个即可,还可以使用instanceof来确定类型

寄生构造函数模式
与工厂模式一样,instanceof操作符不能用来确定对象类型,在能使用其他模式的情况下不推荐使用
稳妥构造函数模式
function Person(name, age) {
      var o = {}
      o.say = function() {
          console.log(name)
      }
      return o
  }

  var p1 = Person("alice", 20)
  p1.say() // alice
这样变量Person中保存的是一个稳妥对象,除了调用say()外,无法访问其数据成员,这种模式instanceof也没什么意义
继承 借用构造函数

使用call或者apply,将父对象的构造函数绑定在子对象上

function Animal() {
      this.species = "猫科动物"
  }

  function Cat(name, color) {
      Animal.apply(this, arguments)
      this.name = name
      this.color = color
  }

  var cat1 = new Cat("kit", "white")
  console.log(cat1.species) // 猫科动物

方法都在构造函数中定义,函数无法复用,借用构造函数很少多带带使用

原型链

使用prototype属性,如果子构造函数的prototype对象指向一个父构造函数的实例,那么所有子构造函数的实例都可以继承父构造函数了

    function Animal() {
      this.species = "猫科动物"
  }

  function Cat(name, color) {
      this.name = name
      this.color = color
  }

  Cat.prototype = new Animal()

  Cat.prototype.constructor = Cat

  var cat1 = new Cat("ly", "blue")
  console.log(cat1.species) // 猫科动物

解析上述代码

Cat.prototype = new Animal()

将Cat的prototype对象指向一个Animal的实例,它相当于删除了prototype原先的值,然后赋予一个新值

- `Cat.prototype.constructor = Cat`任何一个prototype都有一个constructor属性,由于上述代码,会导致Cat.prototype.constructor指向Animal,需要重新将构造函数定义回Cat
- 如果替换了prototype对象那么下一步必定是将constructor指向原来的构造函数
* 由于不能在不影响所有对象实例的情况下传参和由于原型中包含的引用类型值得问题,很少会多带带使用原型链
原型式继承(用空对象做中介)
  function Animal() {
      this.species = "猫科动物"
  }

  function Cat(name, color) {
      this.name = name
      this.color = color
  }

  function F() {}

  F.prototype = new Animal()
  Cat.prototype = new F()

  var cat1 = new Cat("ly", "blue")

  console.log(cat1.species) // 猫科动物
  console.log(Animal.prototype.constructor) // Animal的源代码
function Animal() {
      this.species = "猫科动物"
  }

  function Cat(name, color) {
      this.name = name
      this.color = color
  }

  function extend(child, parent) {
      var F = function() {}
      F.prototype = new parent()
      child.prototype = new F()
      child.prototype.constructor = child
      child.uber = parent.prototype
  }

  extend(Cat, Animal)

  var cat1 = new Cat("jack", "orange")

  console.log(cat1.species) // 猫科动物
这个extend函数,就是YUI库如何实现继承的方法  

最后一行只是为了实现继承的完备性,纯属备用性质

寄生继承

创建一个仅用于封装继承过程的函数

无法实现函数复用

    function creatAnother(obj) {
      var clone = Object(obj)
      clone.say = function() {
          console.log("hi")
      }
      return clone
  }

  var Person = {
      name: "nico"
  }

  var an = creatAnother(Person)
  an.say() // hi
组合继承 - 最常用方式

也叫伪经典继承,将原型链和借用构造函数的技术整合一起,从而发挥二者之长

通过原型链实现对原型属性和方法的继承,通过借用构造函数来实现对实例属性的继承

最常用方式

函数可复用,可传参,不存在引用属性共享问题

function Person(name, age) {
      this.name = name
      this.age = age
  }

  Person.prototype.sayName = function() {
      console.log(this.name)
  }

  Person.prototype.sayAge = function() {
      console.log(this.age)
  }

  function Girls(name, age) {
      Person.apply(this, arguments)
  }

  Girls.prototype = new Person()
    Girls.prototype.constructor = Girls;

  var alice = new Girls("alice", 16)
  alice.sayName() // alice
  alice.sayAge() // 16
寄生组合式继承

最好的继承方式

  function inter(Super, Sub) {
      var clone = Object(Super.prototype) // 创建对象
      clone.constructor = Sub // 增强对象
      Sub.prototype = clone // 指定对象
  }

  function Person(name, age) {
      this.name = name
      this.age = age
  }

  Person.prototype.say = function() {
      console.log(this.name, this.age)
  }

  function Girls(name, age, play) {
      Person.apply(this, arguments)
      this.play = play
  }

  inter(Person, Girls)

  Girls.prototype.plays = function() {
      console.log(this.play)
  }

  var alice = new Girls("alice", 16, "game")
  alice.say() // alice 16
  alice.plays() // game

inter函数实现了寄生组合的最简单形式,在函数内部先创建超类原型副本,为创建的副本添加constructor属性,最后将副本赋给子类型的原型

浏览器环境 DOM和BOM BOM
浏览器对象模型,提供了独立于浏览器显示内容而与浏览器窗口进行交互的对象
location

用于存储当前页面URL信息的对象

location的属性如下:

    //假设url为 http://search.phpied.com:8080/search?p=javascript#results
    location.href = "http://search.phpied.com:8080/search?p=javascript#results";
    location.hash = "";
    location.host = "search.phpied.com:8080";
    location.hostname = "search.phpied.com";
    location.pathname = "/search";
    location.port = "8080";
    location.protocol = "http:";
    location.search = "?p=javascript";

location的三个方法:

reload(): 无参数,重新载入当前页面; location = location也能用于重新载入当前页面

assign(newURL): 载入新页面会留下历史记录

replace(newURL): 载入新页面不会留下历史记录

histroy

存储了页面的访问记录

window.history.lenght: 存储的记录数

history.forward(): 前进

history.back(): 后退

history.go(): 0重新载入当前页面; 正值前进几个页面; 负值后退几个页面

screen

提供了桌面信息

screen.width,screen.height: 桌面分辨率,总大小

screen.availWidth,screen.availHeight: 除去操作系统菜单栏(例如windows的任务栏)以外的区域大小

alert,prompt,confirm
这几个并不属于ECMAScript,而是BOM方法
定时器
定时器也是BOM方法
DOM
文档对象模型,将xml或html文档解析成树形节点的方法
DOM节点
DOM节点属性

nodeName : 节点的名称

nodeValue :节点的值

nodeType :节点的类型

DOM访问快捷方式

getElementsByTagName、getElementsByName、getElementById

可以用属性形式访问attribute,因为class为保留字,所以class要用className访问

兄弟节点、body元素、首尾子节点

属性:nextSibling与previousSibling,得到下一个兄弟节点、上一个兄弟节点;空行也算

属性:nextElementSibling 与previousElementSibling,得到下一个上一个兄弟元素节点

修改节点

innerHtml,可用于修改节点内容

style属性用于修改样式

创建节点

document.createElement(): 创建元素节点

document.createTextNode: 创建文本节点

克隆节点

cloneNode(): 该方法有一个参数,true深拷贝,包括所有子节点, false浅拷贝,只针对当前节点

var odv = document.getElementsByTagName("div")[0];
var p2 = document.getElementsByTagName("p")[1];
odv.appendChild(p2.cloneNode(false));
odv.appendChild(p2.cloneNode(true));
// 浅拷贝,文本节点不会拷贝
插入节点

appendChild(newChild): 将节点插入到 节点的子节点列表的末尾

insertBefore(newChild, refChild): 将节点插入节点指定子节点的前面

var odv = document.getElementsByTagName("div")[0];
var p2 = document.getElementsByTagName("p")[1];
odv.appendChild(p2.cloneNode(true));
odv.insertBefore(p2.cloneNode(true), document.getElementsByTagName("p")[0]);
移除、替换节点

removeChild(child): 从子节点列表中移除指定节点

replaceChild(newChild, oldChild): 将指定子节点替换成新节点

var odv = document.getElementsByTagName("div")[0];
var p2 = document.getElementsByTagName("p")[1];
odv.replaceChild(document.createElement("li"), p2);
只适用于HTML的DOM方法
访问文档的基本方式

document.body

document.images, 相当于Core DOM组件的document.getElementsByTagName("img")的调用

document.forms, 获取所form,包含子元素;然后可以通过elements来访问子元素,如果form或者子元素有名字,也可用过名字访问

var forms = document.forms[0];
// var user = forms.elements[0]; 和下行一样
var user = forms.user;
console.log(user);
user.value = "admin";
//  标签里有了admin
CookiesTitleReferrerDomain
事件 DOM的事件监听

addEventListener(even, function, boolean) : 第一个参数为事件名不加on,第二个参数为函数,第三个为布尔值默认为false在冒泡阶段执行,true在捕获阶段执行

removeEventListener(): 该方法与上一个参数相同,它是移除监听器;但若是第二个参数为匿名函数则移除不了

捕捉和冒泡

捕捉:事件先发生在document上,依次传递给body等,最终达到该元素上

冒泡:事件先发生在该元素上,再依次向上,然后到body,直至document对象上

阻断事件传播

stopPropagation(): 这样就使得事件无法传播了,只发生在自己身上

var op = document.getElementsByTagName("p");
op[0].addEventListener("click", function(e) {
    console.log(e.target);
    e.stopPropagation();
}, false);
阻止默认事件

pereventDefault(): 不是所有默认事件都能阻止

var alink = document.getElementsByTagName("a");
alink[0].addEventListener("click", function(e) {
    e.preventDefault();
    console.log(123);
}, false);
跨浏览器事件监听(兼容IE)
DOM2属性 IE对应属性 说明
addEventListener attachEvent 事件监听
event window.event IE中的全局事件对象
target srcElement 事件元素的target属性
stopPropagation cancelBubble only-IE属性,设置为true
preventDefault returnValue only-IE属性,设置为false
removeEventListener detachEvent 移除事件监听
编程模式与设计模式 编程模式 行为隔离
就是HTML、CSS和JS分开
命名空间
为了减少命名冲突,通常减少全局变量的使用;更好的方法是将不同变量和方法定义在不同命名空间中;本质是只定义一个全局变量,并将其它变量和方法定义为该变量的属性
将对象用作命名空间
// 新建全局变量MYAPP
var MYAPP = MYAPP || {};

// 添加属性
MYAPP.event = {};

// 添加方法
MYAPP.event = {
  getEvent: function(e) {
    // ......
  },
  // ......other methods
}
命名空间中构造器应用
我们可以在命名空间中使用构造器函数
// 本例中,我们用Element构造器创建一个dom元素
var MYAPP = MYAPP || {};
MYAPP.dom = {};
MYAPP.dom.Element = function(type, props) {
    var tmp = document.createElement(type);
    for (var i in props) {
        tmp.setAttribute(i, props[i]);
        console.log(i, props[i]);
    }
    return tmp;
}

var a = new MYAPP.dom.Element("a", {
    href: "javascript.void(0);"
});

document.body.appendChild(a);
namespace方法
var MYAPP = {};

MYAPP.namespace = function(str){
    var parts = str.split("."),
    parent = MYAPP,
    i=0,
    l=0;

    if(parts[0]==="MYAPP"){
        parts = parts.slice(1);
    }
    for(i=0,l=parts.length; i
设计模式
设计模式有23种甚至更多,下面为4种常用模式
单例模式 工厂模式 装饰器模式 观察者模式
var sub = {
    callbacker: [],
    // 发布
    add: function(fn) {
        this.callbacker.push(fn);
    },
   // 订阅
    fire: function(fn) {
        this.callbacker.forEach(function(element) {
            element();
        });
    }
}

sub.add(function() {
    console.log(1)
});
sub.add(function() {
    console.log(2)
});

sub.fire(); // 1 2
ES6 let和const let

let用于申明变量,并且只在该代码块内有效

let不存在变量提升,只能先声明再使用

暂时性死区,在代码块内未使用let申明变量前,变量都是不可用的

不允许重复声明变量

let和const实际上为js提供了块级作用域

const

const就是常量

一旦声明,必须就赋值,且不可变更

无法重复声明

全局对象的属性

全局对象是最顶层的对象,在浏览器环境中是window对象,在nodeJS中是global对象

es5中全局对象属性与全局变量等价

es6中var,function命令声明的全局变量依然是全局对象的属性;而let、const、class命令声明的全局变量则不再是全局对象的属性

变量的解构赋值 数组的结构赋值 基本用法
let [a, b, c] = [1, "a", ["c"]];
console.log(a, b, c); // 1, "a", ["c"]

即匹配模式,等号两边格式一样,即可进行变量的赋值

若右边表达式不是数组,则会报错;只要有Iterator接口的数据结构都可以使用数组形式的解构赋值

适用于var、let、const命令

若解构失败变量值为undefined

默认值

解构赋值允许有默认值

ES6内部使用严格相等运算符(===),所以一个数组成员不严格等于undefined是不会使用默认值的

var [a = 1, b = 2, c = 3, d = 4] = [undefined, null, "c", d];
console.log(a, b, c, d); //1 null "c" 4
对象的解构赋值
    var {x, y} = {x: 1, y: "a" };
    console.log(x, y); //1 "a"

    
    var { bar: baz } = { bar: "hello" };
    console.log(baz); //hello


    /*下面例子中js会将{}当成代码块,此时需要用小括号括起来*/
    var k;
    // {k} = {k: 123};会报错,js会将{}当成代码块,此时需要用小括号括起来
    ({ k } = { k: 123 });
    console.log(k); // 123


    /*在这个例子中,loc是模式不是变量不会赋值*/
    var local = {
        loc: {
            ui: "ui",
            txt: "txt"
        },
        title: "标题"
    }

    var {
        loc: {
            ui,
            txt
        },
        title
    } = local;
    // console.log(loc); loc is not defined
    console.log(ui, txt, title); // ui txt 标题


    /*对象的解构赋值也能设置默认值,同样只有在值严格为undefined时,默认值才会有用*/
    var {bat = 123, bas = 456, bad} = {bat: undefined, bas: null, bad: "bad"};
    console.log(bat, bas, bad); // 123 null "bad"


    /*若解构失败变量值为undefined*/


    /*对象的解构赋值,可以方便的将对象方法赋值到某个变量中*/
    var {pow, random} = Math;
    // 将Math对象的次方和随机数方法赋值给了pow和random
    console.log(pow(2, 3)); // 8
    console.log(random()); // 随机数


    /*因为数组也是特殊对象,因此数组也能进行对象的解构赋值*/
    var arr = [1, 2, 3];
    var {0: first, [arr.length - 1]: last} = arr;
    console.log(first, last); // 1 3
字符串的解构赋值
// 将字符串转成了类似数组的对象
const [a, b] = "hi";
console.log(a, b); // h i

// 字符串有length属性,因此可以对这个对象进行解构赋值
var {
    length: len
} = "hello";
console.log(len); // 5
函数参数的解构赋值
    function add([x, y]) {
        return x + y;
    }
    console.log(add([1, 2])) // 3

    function move({
        x = 0,
        y = 0
    } = {}) {
        return [x, y];
    }
    console.log(move({
            x: 3
        })) // [3, 0]
    console.log(move({
            y: 8
        })) // [0, 8]
    console.log(move({
            x: 3,
            y: 8
        })) // [3, 8]
解构赋值的应用

交换变量值

[x, y] = [y, x];

从函数返回多个值

函数参数定义和设置默认值

遍历Map结构

输入模块的指定方法

提取JSON

var data = {
        id: 1,
        name: "admin",
        type: 0,
        data: {
            goods: [9001, 9002],
            goods_type: [0, 1]
        }
    }

    var {
        id,
        name,
        type,
        data: {
            goods,
            goods_type
        }
    } = data;
    console.log(id, name, type, goods, goods_type); // 1 "admin" 0 (2) [9001, 9002] (2) [0, 1]
数组的扩展 新增方法 Array.from
Array.from方法用于将类似数组的对象和可遍历对象(包含set、map)转换成数组

实际应用中可以将DOM操作返回的NodeList和函数内arguments转为数组

还能接受第二个参数,作用类似于数组的map方法,对每个元素进行处理,将处理后的值放入返回的数组

Array.of

用于将一组值转换为数组

var arr1 = Array();
var arr2 = Array(3);
var arr3 = Array(1, 2);
console.log(arr1); // []
console.log(arr2); // [ , , ]
console.log(arr3); // [1, 2]

var arr4 = Array.of(1);
console.log(arr4); // [1]

/*因为数组的构造函数,只有在参数不小于2时才会作为数组值,一个参数时作为数组长度;Array.of则是保持一致,无论参数多少都作为数组元素*/
copyWithin

copyWithin(target, start, end): 将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组,也就是会修改当前数组

参数:

target(必须): 从该位置开始替换

start(可选):从该位置读取,默认为0,若为负值则为倒数

end(可选):读取到该位置结束,默认为数组长度,若为负值则为倒数

find和findIndex

参数为一个函数,将数组内元素依次执行该回调函数,找出第一个返回值为true的成员,find是返回这个成员,若没有符合条件的成员则返回undefined;findIndex是返回该成员位置,若没有符合条件的成员则返回-1

回调函数,接收三个参数:当前值、当前位置、原数组

fill

用给定值,填充一个数组,会覆盖原有全部值

可以接受第二、三个参数,用于指定填充的起始和结束位置

数组实例的keys、values、entries

都用于遍历数组,返回一个遍历器,可用for...of来遍历

keys()是对键名,values()是对键值,entries()是对键值对

数组实例的includes

includes返回一个布尔值,表示数组是否包含给定的值

该方法可选第二个参数,代表起始搜索位置,默认为0,若为负值则倒数

[1, 2, 3].includes(2) // true
函数扩展 函数的默认值

可以设置函数默认值,有默认值的参数最好作为尾参

可使用rest参数, 即...参数

    function add(...val) {
        let sum = 0;
        for (let i of val) {
            sum += i
        }
        console.log(sum);
    }

    add(1, 2, 3, 4, 5, 6, 7, 8, 9); //45

rest参数是一个数组,可以使用任意数组方法

rest参数后面不能再跟任何参数

rest参数可以替代Array.prototype.slice.call(arguments)

扩展运算符 基本含义

扩展运算符就是三个点...,用于将数组转为用逗号分隔的参数序列

    function add(...val) {
        let sum = 0;
        for (l           
               
                                           
                       
                 

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/107742.html

相关文章

  • Javascript判断数组是否包含特定元素方法汇总

    摘要:具体内容请参考。感谢大家阅读,另外,在这边帮朋友推一个爱心众筹,希望大家能够奉献点爱心,朋友母亲,身患直肠癌,目前在北京武警总医院接收治疗,可留言留下您的联系方式,日后感激大家 判断数组是否包含某一特定元素是很常见的需求,javascript中有很多实现方法,今天有空汇总了一下,按兼容性由强到弱排序,返回类型一律为boolean: 假设数组为arr,目标元素为target 循环遍历: ...

    impig33 评论0 收藏0
  • javascript字符串方法学习汇总

    摘要:返回字符串中指定位置的字符返回指定位置的字符的编码输出用于连接多个字符串。输出方法用于把一个字符串分割成字符串数组。返回布尔值,表示参数字符串是否在原字符串的头部。 1、charAt(index):返回字符串中指定位置的字符; charCodeAt(index):返回指定位置的字符的Unicode编码 var str = abcdefghi; console.log(str.cha...

    microelec 评论0 收藏0
  • 前端资源系列(4)-前端学习资源分享&前端面试资源汇总

    摘要:特意对前端学习资源做一个汇总,方便自己学习查阅参考,和好友们共同进步。 特意对前端学习资源做一个汇总,方便自己学习查阅参考,和好友们共同进步。 本以为自己收藏的站点多,可以很快搞定,没想到一入汇总深似海。还有很多不足&遗漏的地方,欢迎补充。有错误的地方,还请斧正... 托管: welcome to git,欢迎交流,感谢star 有好友反应和斧正,会及时更新,平时业务工作时也会不定期更...

    princekin 评论0 收藏0
  • 前端相关汇总

    摘要:简介前端发展迅速,开发者富有的创造力不断的给前端生态注入新生命,各种库框架工程化构建工具层出不穷,眼花缭乱,不盲目追求前沿技术,学习框架和库在满足自己开发需求的基础上,然后最好可以对源码进行调研,了解和深入实现原理,从中可以获得更多的收获随 showImg(https://segmentfault.com/img/remote/1460000016784101?w=936&h=397)...

    BenCHou 评论0 收藏0
  • javascript前端面试题汇总

    摘要:最后代码执行代码执行,,调用优先顺序成员访问带参数列表函数调用无参数列表查找一个字符串中指定字符出现的位置经典问题 1、 JavaScript中如何检测一个变量类型是String?请写出函数实现 //分析:String的两种创建方法: //第一种方法: var str = str //str只是一个以String为数据类型的值,但并不属于String对象的实例 //第二种方法: var...

    RancherLabs 评论0 收藏0
  • 你不能错过的前端面试题合集

    摘要:收集的一些前端面试题从面试题发现不足,进而查漏补缺,比通过面试更难得及各大互联网公司前端笔试面试题篇及各大互联网公司前端笔试面试题篇面试题个和个经典面试题前端开发面试题如何面试前端工程师很重要个变态题解析如何通过饿了么面试轻 收集的一些前端面试题 从面试题发现不足,进而查漏补缺,比通过面试更难得 1 BAT及各大互联网公司2014前端笔试面试题--Html,Css篇 2 BAT...

    ninefive 评论0 收藏0

发表评论

0条评论

用户83

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<