javaScript高级程序设计第四版笔记

1,091 阅读14分钟

let 和var区别

let声明的范围是块作用域,var声明范围是函数作用域;

let也不允许同一个块作用域中出现冗余声明;

对声明冗余报错不会因混用letvar而受影响,这两个关键字声明的并不是不用类型的变量,他们只是指出变量在相关作用域如何存在;

var可以变量提升;let 不可以(严格来讲,let也会被提升),在let声明之前的执行瞬间被称为“暂时性死区”

if(true){
    var name = 'chc';
    var name;
    let name;//SyntaxError
    let age = 18;
    let age; //SynctaxError;标识符age已经声明过了
    var age;//SyntaxError
    console.log(name);//chc
    console.log(age);//18
}
console.log(name);//chc
console.log(age);//ReferenceError:age没有定义

const

使用cosnt声明变量必须同时初始化为某个值,已经声明,在其生命周期的任何时候都不能在重新赋予新值;

赋值为对象的const变量不能再被重新赋值为其他引用值,但对象的键则不受限制

const a = {};
a = {};//TypeError
const b = {};
b.name = 'chc';
console.log(b.name);//‘chc'

如果想让整个对象都不能修改,可以使用Object.freeze(),这样再给属性赋值时虽然不会报错,但会静默失败:

const c = Object.freeze({});
c.name = 'chc';
console.log(c.name);//undefined

javaScript变量类型

javaScript变量可以保存两种类型的值:原始值和引用值。

原始值可能是以下6种数据类型之一:

  • Underfined
  • Null
  • Boolean
  • Number
  • String
  • Symbol

引用值:

  • Object

原始值和引用值特点如下:

  • 原始值大小固定,因此保存在栈内存上
  • 从一个变量到另一个变量复制原始值会创建该值的第二个副本
  • 引用值是对象,存储在堆内存中
  • 包含引用值得变量实际上只包含指向相应对象得一个指针,而不是对象本身
  • 从一个变量到另一个变量复制引用值只会复制指针,因此结果是两个变量都指向同一个对象
  • typeof操作符可以确定值的原始类型,而instanceof操作符用于确保值的引用类型

数值的方法

数值格式化为字符串(toFixed())

  • toFixed()方法返回包含指定小数点位数的数值字符串,如果数值本身的小数位超过了参数指定的位数,则四舍五入到最接近的小数位:

    let num = 10;
    console.log(num.toFixed(2));//'10.00'
    let num2 = 10.005;
    console.log(num2.toFixed(2));//'10.01'
    
  • toExponential(),返回以科学计数法表示的数值字符串,接受一个参数,表示结果中的小数的位数

    let num = 10;
    console.log(num.toExponential(1));//'1.0e+1'
    console.log(num.toExponential(2));//'1.00e+1'
    
  • toPrecision()方法会根据情况返回最合理的输出结果,可能是固定长度,也可能是科学计数法形式。接受一个参数,表示结果中数字的总位数(不包含指数)

    let num = 99;
    console.log(num.toPrecision(1));//'1e+2'
    console.log(num.toPrecision(2));//'1.0e+2'
    

本质上,toPrecison()方法会根据数值的精度来决定调用toFixed()还是toExponential()。为了以正确的小数位精确表示数值,这三个方法都会向上或向下舍入。

isInteger()和isSafeInteger()

  • Es6新增了Number.isInteger()方法,用于辨别一个数值是否保存为整数。

    console.log(Number.isInteger(1));//true
    console.log(Number.isInteger(1.000));//true
    console.log(Number.isInteger(1.01));//false
    
  • Number.MIN_SAFE_INTEGER(-2^53+1) ~ Number.MIN_SAFE_INTEGER(2^53-1)为IEEE754数值格式的一个特殊的数值范围,为鉴别整数是否在这个范围内,可以使用Number.isSafeInteger()方法:

    console.log(Number.isSafeInteger(数值))//true or false
    

字符串的方法

字符串位置方法(indexOf())

有两个方法用于在字符串中定位子字符串:

  • indexOf()方法从字符串开头查找子字符串

    let stringValue = 'hello world'
    console.log(stringValue.indexOf('o'));//4
    //第二个参数表示开始查找的位置
    console.log(stringValue.indexOf('0',6));//7
    
  • lastIndexOf()方法从字符串末尾开始查找子字符串

    let stringValue = 'hello world';
    console.log(stringValue.lastIndexOf('o'));//7
    //第二个参数表示从末尾开始往前数排除在查找范围的字符串
    console.log(stringValue.lastIndexOf('0',6));//4
    

判断字符串是否包含子字符串

ECMAScript6增加了3个用于判断子字符串是否包含另一个字符串的方法:

  • startsWith()检查开始于索引0的匹配项

  • endsWith()检查开始于索引(string.length - substring.length)的匹配项,

  • includex()检查整个字符串

    let message = 'foobarbaz'
    console.log(message.startsWith('foo'));//true
    console.log(message.startsWith('bar'));//false
    console.log(message.endsWith('baz'));//true
    console.log(message.endsWith('bar'));//false
    console.log(message.includes('bar'));//true
    console.log(message.includes('qux'));//false
    
  • startsWith(),includes()方法添加第二个参数,表示开始搜索的位置。如果传入第二个参数,则意为着这两个方法会从指定位置向字符串末尾搜索,忽略该位置之前的所有字符。

    let message = 'foobarbaz';
    console.log(message.startsWith('foo'));//true
    console.log(message.startsWith('foo',1));//false
    console.log(message.includes('bar'));//true
    console.log(message.includes('bar',4))//false
    
  • endsWith()方法接收第二个参数,表示应该当作字符串末尾的位置。如果不提供这个参数,那么默认就是字符串的长度。如果提供这个参数,那么就好像字符串只有那么多字符一样:

    let message = 'foobarbaz';
    console.log(message.endsWith('bar'));//false
    console.log(message.endsWith('bar',6));//true
    

trim()方法

这个方法会创建字符串的一个副本,删除前后所有的空格,在返回结果。

另外,trimLeft()和trimRight()方法分别用于从字符串开始和末尾清理空格符

repeat()方法

这个方法接收一个整数参数,表示要将字符串复制多少次,然后返回拼接所有副本后的结果。

padStart()和padEnd()方法

1.该连个方法会复制字符串,如果原字符串小于指定长度,则在相应一边填充字符,直至满足长度条件;

2.这连个方法的第一个参数是长度,第二个参数是可选的填充字符串,默认为空格

3.可选的第二个参数并不限于一个字符,如果提供了多个字符的字符串,则会将提供的字符串拼接并截断以匹配指定的长度。

4.此外,如果长度小于或等于原字符串长度,则会返回原始字符串

let strigValue = 'foo'
console.log(stringValue.padStart(6));//'   foo'//foo前有三个空格
console.log(stringValue.padStart(9,'.'));//'......foo'
console.log(stringValue.padEnd(6));//'foo   '//foo后有三个空格
console.log(stringValue.padEnd(9,'.'));//'foo......'

console.log(stringValue.padStart(8,'bar'));//'barbafoo'
console.log(stringValue.padStart(2));//'foo'
console.log(stringValue.padEnd(8,'bar'));//'foobarba'
console.log(stringValue.padEnd(2));//'foo'

join()方法

join()方法接收一个参数,即字符串分隔符,返回包含所有项的字符串。来看下面的例子:

let colors = ['red','blue','green']
console.log(colors.join(","));//red,blue,green
console.log(colors.join("||"));//red||blue||green

replace()方法

这个方法接收两个参数:

1.第一个参数可以是一个RegExp对象或一个字符串,

2.第二个参数可以是一个字符串或一个函数

let text = 'cat,bat,sat,fat';
let result = text.replace('at','ond');
console.log(result);//'cond,bat,sat,fat'
let result2 = text.repace(/at/g,'ong');
console.log(result2);//'cond,bond,sond,fond'

replace()的第二个参数可以是一个函数。在只有一个匹配项时,这个函数会收到三个参数:

  • 与整个模式匹配的字符串

  • 匹配项在字符串中的开始位置

  • 整个字符串

function htmlEscape(text){
	return text.replace(/[<>"&]/g,function(match,pos,originalText){
		//console.log(march)//< " " > < >
        switch(match){
			case '<':return '<';
			case '>':return '>';
			case '&':return '&amp;';
			case '\"':return '&quot;';
		}
	})
}
console.log(htmlEscape('<p class=\"greeting\">hello world</p>'));
//<p class=&quot;greeting&quot;>hello world</p>

localeCompare()方法

这个方法比较两个字符串,返回如下3个值得一个:

  • 如果按照字母表顺序,原字符串应排在参数字符串前头,则返回负值 -1

  • 如果两个比较得字符串相等,则返回0

  • 如果按照字母表顺序,原字符串应排在参数字符串后头,则返回正值 1

  • 如果第一个字母相等则比较下一个,一直到字符串末尾

    let sv = 'yellow';
    console.log(sv.localeCompare('brick'));//1
    console.log(sv.localcompare('yellow'));//0
    console.log(sv.localcompare('yao'));//1
    console.log(sv.localcompare('yfo'));//-1
    console.log(sv.localcompare('zoo'));//-1
    

URL编码方法

encodeURI()encodeURIComponent()方法用于编码统一资源标识符:

encodeURI()方法用于对整个URI进行编码;

encodeURIComponent()方法用于编码URI中单独得组件;

这连个方法得主要区别是:

encodeURI()不会编码属于URI组件得特殊字符,比如冒号,斜杠,问号,井号。

encodeURIConponent()会编码他发现的所有非标准字符。

decodeURI()decodeURIComponent()与上述两个方法相对,

decodeURI()只对使用encodeURI()编码过的字符解码

decodeURIComponent()解码所有被encodeURIComponent()编码的字符,基本上就是解码所有特殊值。

Math

ECMAScript提供了Math对象作为保存数学公式,信息和计算的地方。

Math对象提供了一些辅助计算的属性和方法。

min()和max()方法

min()和max()方法用与确定一组数值中的最小值和最大值。可接收任意多个参数

let max = Math.max(3,54,32,16);
console.log(max);//54
let min = Math.min(3,54,32,16);
console.log(min);//3

舍入方法

  • Math.ceil()方法始终向上舍入为最接近的整数。
  • Math.floor()方法始终向下舍入最接近的整数。
  • Math.round()方法执行四舍五入
  • Math.fround()方法返回数值最接近的单精度(32位)浮点值表示

random()方法

Math.random()方法返回一个0~1范围内的随机数,其中包含0但不包含1。

let num = Math.floor(Math.random()*10+1);//1~10的值

如果是为了加密而需要生成随机数(传给生成器的输入需要较高的不确定性),那么建议使用window.crypto.getRandomValues()

Array

1.Array.from()用于将类数组结构转换为数组实例:

  • 该方法的第一个参数是一个类数组对象,即任何可迭代的结构,或者有一个length属性和可索引元素的结构。
//字符串会被拆分为但字符数组
console.log(Array.from('matt'));//['m','a','t','t']
console.log(Array.from(1,2,3,4));//[1,2,3,4]
  • 第二个可选的映射函数参数。这个函数可以直接增强新数组的值。
  • 第三个可选参数,用于指定映射函数中this的值。但这个重写的this值在箭头函数中不适用
const a1 = [1,2,3,4];
const a2 = Array.from(a1,x=>x**2);
const a3 = Array.from(a1,function(x){
    return x**this.exponent
},{exponent:2})
console.log(a2);//[1,4,9,16]
console.log(a3);//[1,4,9,16]

2.Array.of()用于将一组参数转换为数组实例

​ 该方法可以把一组参数转换为数组,这个方法用于替代在ES6之前常用的Array.prototype.slice.call(arguments),一种异常笨拙的将arguments对象转换为数组的写法:

console.log(Array.of(1,2,3,4));//[1,2,3,4]
//和Array()做对比
Array.of(1);//[1]
Array(1);//表示创建了长度为1的空数组
console.log(Array.of(undefined));//[undefined]

3.Array.isArray()方法用于判断一个值是否是数组

迭代器方法

  • keys()返回数组索引的迭代器

  • values()返回数组元素的迭代器

  • entries()返回索引/值对的迭代器

const a = ['foo','bar','baz','qux']
console.log(Array.from(a.keys()))//[ 0, 1, 2, 3 ]
console.log(a.values())//Object [Array Iterator] {}

//Object [Array Iterator] {}
//[[ 0, 'foo' ],[ 1, 'bar' ],[ 2, 'baz' ],[ 3, 'qux' ]]
console.log(a.entries())
for(let item of a.entries()){
	console.log(item)//[ 0, 'foo' ],[ 1, 'bar' ],[ 2, 'baz' ],[ 3, 'qux' ]
}

复制和填充方法

copyWithin()批量复制方法,fill()填充数组方法

这两个方法的函数签名类似,都需要指定既有数组实例上的一个范围,包含开始索引,不包含结束索引。使用这个方法不会改变数组的大小。

fill()方法

fill()方法可以向一个已有的数组中插入全部或部分相同的值。

  • 开始索引用于指定开始填充的位置,它是可选的。

  • 如果不提供结束索引,则一直填充到数组末尾。

  • 负值索引从数组末尾开始计算。也可以将负索引想象成数组长度加上它得到的一个正索引:

const zeroes = [0,0,0,0,0];
//用五填充整个数组
zeroes.fill(5);
console.log(zeroes);//[5,5,5,5,5]
zeroes.fill(0)//重置
//用6填充索引大于等于3的元素
zeroes.fill(6,3)
console.log(zeroes)//[0,0,0,6,6]
zeroes.fill(0)//重置
zeroes.fill(7,1,3)//用7填充大于等于1且小于3的元素
console.log(zeroes)//0,7,7,0,0]
zeroes.fill(0)//重置
//用8填充大于等于1且小于4的元素
zeroes.fill(8,-4,-1);
console.log(zeroes);//[0,8,8,8,0]
zeroes.fill(4,3,10)
console.log(zeroes);//[0,0,0,4,4]

copyWithin()方法

copyWithin()会按照指定范围浅复制数组中的部分内容,然后将它们插入到指定索引开始的位置。

开始索引和结束索引则与fill()使用同样的计算方法:

ints = [0,1,2,3,4,5,6,7,8,9]
ints.copyWithin(5);
console.log(ints);//[0,1,2,3,4,0,1,2,3,4]
ints.copyWithin(0,5);
console.log(ints);//[5,6,7,8,9,5,6,7,8,9]
ints.copyWithin(4,0,3);
console.log(ints);//[0,1,2,3,0,1,2,7,8,9]
ints.copyWithin(-4,-7,-3);
console0.log(ints);//[0,1,2,3,4,5,3,4,5,6]

栈方法

push()方法和pop()方法

push()方法接收任意数量的参数,并将它们添加到数组末尾,返回数组的最新长度。

pop()方法则用于删除数组的最后一项,同时减少数组的length值,返回被删除的项。

let colors = new Array();
let count = colors.push('red','green');
console.log(count)//2
count = colors.push('black');
console.log(count)//1
let item = colors.pop();
console.log(item)//black
//通过下表添加
colors[3] = 'black'

队列方法

shift()方法和unshift()方法

shift()方法,它会删除数组的第一项并返回它,然后数组长度减一

unshift()就是执行跟shift()相反的操作:在数组开头添加任意多个值,然后返回新的数组长度。

let colors = ['red','blue','green']
let item = colors.shift();
console.log(item)//red
console.log(colors);//['blue','green']
let count = colors.unshift('black');
console.log(count);//1
console.log(colors);//['black','blue','green']

排序方法

reverse()方法

reverse()方法就是将数组元素反向排序

let values = [1,2,3,4,5]
values.reverse();
console.log(values);//5,4,3,2,1

sort()方法

  • sort()会按照升序重新排列数组元素,即最小的值在前面,最大的值在后面

  • 为此,sort()会在每一项上调用String()转型函数,然后比较字符串来决定顺序。

  • 即使数组的元素都是数值,也会先把数组转换为字符串再比较、排序。

let values = [0,1,5,10,15]
values.sort();
console.log(values);//0,1,10,15,5

为了更好的解决排序问题,sort()方法可以接收一个__比较函数__。

比较函数接收两个参数:

  • 如果第一个参数应该排在第二个参数前面,就返回负值;
  • 如果两个参数相等,就返回0;
  • 如果第一个参数应该排在第二个参数后面,就返回正值。
//升序,把返回值改下就能实现升序
function compare(value1,value2){
    //第一个形参value1代表的实参始终在value2所代表的后面
    console.log(value1)//1,5,10,15
    console.log(value2)//0,1,5,10
    if(value1 < value2){
        return -1
    }else if(value1 > value2){
        return 1;
    }else{
        return 0
    }
}
let values = [0,1,5,10,15]
values.sort(compare);
console.log(values);//0,1,5,10,15
#OR
values.sort((a,b) => a < b ? 1 : a > b ? -1 : 0)
console.log(values)://15,10,5,1,0
#OR
function compare(value1,value2){
    //升序
    return value1 - value2
    //降序
   // return value2 - value1
}
let values = [0,1,5,10,15]
console.log(values.sort(compare))//[0,1,5,10,15]

sort方法第一个参数是函数,其实是一个回调函数,回调函数有两个参数

第一个形参数所代表的实参始终在第二个形参代表的实参的后面

所以应该这样理解,value1 < value2比较的就是目标数组中后一个数是否比前一个数小,如果判定为false,那么return -1,(value1 > value2的判定为true,return 1;其他情况return 0)就能实现目标数组__升序__;如果判定为true,那么return 1就能实现目标数组的__降序__。

function compare(value1,value2){
	console.log(value1)//2,4,3,9,6
	console.log(value2)//1,2,4,3,9
    if(value1 < value2){
        //升序
        return -1;
        #OR
        //降序
        return 1;
    }else if(value1 > value2){
       	//升序
        return 1;
        #OR
        //降序
        return -1;
    }else{
        return 0
    }
}
let data = [
	1,2,4,3,9,6
];
data.sort(compare);

Object

Object.assign()合并对象

这个方法接收一个目标对象和一个或多个源对象作为参数

let dest,src,result1;
dest = {}
src = {id:'src'}
result1 = Object.assign(dest,src)
dest.aaa = 'bbb'
result1.bbb = 'aaa'
console.log(dest,result1)
//{ id: 'src', aaa: 'bbb', bbb: 'aaa' } { id: 'src', aaa: 'bbb', bbb: 'aaa' }

从上面代码可知:

  • Object.assign()实际上对每个源对象执行的是浅复制。

Object.is()

有些特殊情况即使是===操作符也无能为力

这个方法与===很像,必须接收两个参数:

console.log(Object.is(true,1))//false
console.log(Object.is({},{}))//false
console.log(Object.is(+0,-0))//false
console.log(+0 === -0)//true
console.log(Object.is(NaN,NaN))//true
console.log(NaN === NaN)//false

结构赋值

对象的解构

可以在一条语句中使用嵌套数据实现一个或多个赋值操作。

简单地说,对象解构就是使用与对象匹配的结构来实现对象属性赋值。

let person = {
    name:'matt',
    age:19
}
let {name:personName,age:personAge} = person;
console.log(personName);//matt
console.log(personAge);//19
#OR
let {name,age} = person;
console.log(name);//matt
console.log(age);//19

解构赋值不一定与对象的属性匹配。赋值的时候可以忽略某些属性,而如果引用的属性不存在,则该变量的值就是undefined:

let person = {
    name:'matt',
    age:19
}
let {name,job} = person;
console.log(name);//matt
console.log(job);//undefined
#OR
let {name,job = 'aaa'} = person
console.log(name);//matt
console.log(job);//aaa

解构在内部使用函数ToObject()(不能在运行时环境中直接访问)把源数据结构转换为对象

这也意味着(根据ToObject()的定义), null和undefined不能被解构,否则会抛出错误。

解构并不要求变量必须在解构表达式中声明。

不过,如果是给事先声明的变量赋值,则赋值表达式必须包含在一对__括号__中:

let personName,personAge;
let person = {
	name:'matt',
	age:19
};
({name:personName,age:personAge} = person)//必须用括号包裹
console.log(personName,personAge)

未完待续...


参考资料:《javaScript高级程序设计第四版》