basics
变量
-
命名:不能是关键字、要有意义、不能以数字开头、不能包含空格和减号(多个单词拼在一起firstName)、大小写敏感
-
一行定义一个变量
-
没有初始化的变量类型:undefined
// 关于变量
// Cannot be a reserved keyword
// Should be meaningful
// Cannot start with a number (eg:1name)
// Cannot contain a space or hyphen (-)
// Are case-sensitive 对大小写敏感
let name = 'world';
console.log(name);
let firstName = 'The';
let lastName = 'World';
常量
不想重新赋值时可以使用常量,常量不可以修改
// 关于常量
// 常量赋值会报错
const intersetRate = 0.3;
intersetRate = 1
console.log(intersetRate);
JS有2类数据类型:值类型和引用类型
值类型(value types)数据
- String
- Number
NaN表示不是一个数字,但是它的类型是number, 但它是一个不能用来做运算的数字 - Boolean
- undefined 是一个类型,同时是一个值let firstName = undefined;,firstName类型也是undefined
- null 清空一个变量的值,let selectedColor = null; selectedColor类型为object
- Symbol(ES6新增)
引用类型(reference types)数据
- Object
- Array
- Function
let name = 'world'; // String Literal
let age = 1; // Number Literal
let isApproved = false; // Boolean Literal
let firstName = undefined;
let selectedColor = null;
动态语言
静态语言:一个变量被声明,类型不能再更改,string name = ‘princess’;
动态语言:变量类型可以在运行时改变,基于赋值给它的数据,let name = ‘princess’;
- 所有的数都是数字类型,不分浮点数和整数
// 对于静态语言,变量声明之后,其类型无法更改
// 对于动态语言,变量类型可以在运行时发生改变
let name = 'world'; // String Literal
let age = 1; // Number Literal
let isApproved = false; // Boolean Literal
let firstName = undefined;
let selectedColor = null;
对象objects
let person = {//对象包含姓名、年龄两个属性
name:'jack',//一个键值对
age:30
};
//两种访问方法: 点 Dot Notation
person.name = 'John';
//中括号:Bracket Notation
let selection = 'age';
person[selection] = 27;
console.log(person);
中括号访问好处:直到运行时都不知道访问的属性名是什么,在运行时计算出来,eg.客户端,用户可以选择访问的属性目标。
输出结果一样:
let selection = 'name';
person[selection] = 'orange';
console.log(person.name);
数组Arrays
用来保存一组数据(a list of items)的结构
- 数组的长度动态、可变,数组中元素类型也是动态
- 技术上说,数组是一个对象,每创建一个数组,继承了一些属性(原型)
let selectedColors = ['red', 'blue'];
selectedColors[2] = 'green';
console.log(selectedColors.length);
函数Functions
功能:执行一个任务performing a task 或 计算一些值calculating a value
function greet(name, lastName){
console.log('Hello ' + name + ' ' + lastName);
}
greet('John','Smith');
函数的类型
函数是一组语句(a set of statements)
function square(number){
return number * number;
}
console.log(square(2));
oprators
JavaScript操作符
- 代数操作符
- 赋值操作符
- 比较操作符
- 逻辑操作符
- 位元操作符符
代数操作符
let x = 10;
let y = 3;
console.log(x + y);
console.log(x - y);
console.log(x * y);
console.log(x % y);
console.log(x ** y);
// Increment (++)
x = 10;
console.log(x++); // 10
x = 10;
console.log(++x); // 11
//Decrement
x = 10;
console.log(x--); // 10
x = 10;
console.log(--x); //9
赋值操作符
let x = 10;
x +=5;
x *=3;
比较操作符
let x = 1;
// Relational
console.log(x > 0);
console.log(x >= 1);
console.log(x < 1);
console.log(x <= 1);
// Equality
console.log(x === 1);
console.log(x !== 1);
等价操作符
stricte equality( ===) 比较的结果更加准确
lose equality(= =)抽象相等:两边不关心是否同类型,如果类型不一样,它会将右边的类型转为左边的
// Strict Equality (Type + Value) 严格相等(符号两边的type + value 都要完全一样)
console.log(1 === 1);
console.log('1' === 1);
console.log(true === 1);
// Loose Equality (Value) 抽象相等(只检查value 相等)
console.log(1 == 1);
console.log('1' == 1);
console.log(true == 1);
三元运算符
//如果一个顾客有100点就是金牌客户,否则就是银牌客户
let points = 110;
let type = points > 100 ? 'gold' : 'silver';
console.log(type);
逻辑(logical)操作符
1、 与 &&、或 ||、非 !
// 逻辑与(&&)
// 如果两个参数都是TRUE,则返回TRUE
let highIncome = false;
let goodCreditScore = true;
let eligibleForLoan = highIncome && goodCreditScore;
console.log(eligibleForLoan);
// 逻辑或 (||)
// 如果其中一个参数为TRUE,则返回TRUE
eligibleForLoan = highIncome || goodCreditScore;
console.log(eligibleForLoan);
// 非 (!)
let applicationRefused = !eligibleForLoan;
console.log(applicationRefused);
2、在非布尔值中进行逻辑运算
如下,计算表达式时,会查看每个参数,如果参数不是一个逻辑值,就会尝试将值转换为一个类真或者类假值
false || 'Mosh'
返回'Mosh'
false || 1
返回1
Falsy 类假 不是真的布尔类型的假值(false)
可以是undefined, null, 0 , false, ‘’ 空字符串, NaN 非数字
除了以上,其他都是—> Truthy
// Falsy (false) 类假
// undefined
// null
// 0
// false
// ‘’
// NaN
// Truthy (true) 类真
// Anything that is not Falsy 除了以上,其他都是类真
// Short - circuting 短路(就近判断)
let userColor = undefined;
let defaultColor = 'blue';
let currentColor = userColor || defaultColor;//用户选择的颜色或者默认颜色,如果没选就使用默认颜色
console.log(currentColor);
位元(bitwise)操作符
位元的或、与运算: | 、&
实例:假设一个接入控制系统,用户有三种权限:read, write, execute
//read, write, execute
//00000100, 00000110, 00000111
const readPermission = 4; //转换为十进制
const writePermission = 2; //00000010
const executePermission = 1; //00000001
let myPermission = 0;
//使用位或可以添加权限
myPermission = myPermission | readPermission | writePermission;
//使用位与可以查询权限
let message = (myPermission & readPermission) ? 'yes' : 'no';
console.log(message);
运算符优先级
let x = (2 + 3) * 4;
console.log(x)
练习–互换变量
// 交换两个变量的值
let a = 'red';
let b = 'blue';
let c = a;
a = b;
b = c;
console.log(a);
console.log(b);
Flow Control
条件语句:
If…else (用的更多)
// 如果时间是在早上6点到晚上12点之间:早上好
// 如果是在中午12点到下午6点之间:下午好!
// 否则:晚上好!
let hour = 16;
if (hour >= 6 && hour < 12)
console.log('Good morning!');
else if (hour >= 12 && hour < 18)
console.log('Good afternoon!')
else
console.log('Good evening!')
Switch…case
拿多个情况与判断条件变量比较
let role = 'guest'; //代表用户当前的角色
switch(role){
case 'guest':
console.log('Guest User');
break;
case 'moderator':
console.log('Moderator User')
break;
default:
console.log('Unknown User')
}
上面的代码等价于
if (role === 'guest') console.log('Guest User');
else if (role == 'moderator') console.log('Moderator User');
else console.log('Unknown User')
循环Loops:
For
for (let i = 0; i <= 5; i++){
if (i %2 !== 0) console.log(i);
}
While
let i = 0;
while (i <= 5){
if (i %2 !== 0) console.log(i);
i++;
}
Do…while
- 在while语句中,判断先于循环的内容进行
- 在do-while语句中,判断在循环执行结束后进行(至少会执行一次循环体的内容,即使不满足判断条件)
let i = 0;
do {
if (i %2 !== 0) console.log(i);
i++;
} while (i <= 5 )
无限循环(死循环)
忘记写自增++,或者 while( true)
let i = 0;
while (i < 5) {
console.log(i);
i++;
}
while(true) {
}
let x = 0;
do {
}while (x < 5)
for (let i = 1; i > 0; i++)
console.log(i)
For…in
用来遍历一个对象的所有属性
也可遍历数组,但是这个方法不好,遍历数组时:每次迭代过程中index 会被赋值为数组元素的序号
const person = {
name: 'MOsh',
age: 30
};
for (let key in person)
console.log(key, person[key]);
const colors = ['red', 'green', 'blue'];
for (let index in colors)
console.log(index, colors[index])
For…of
for of 只能用于可枚举的类型,如数组和映射(map)
const colors = ['red', 'green', 'blue'];
//迭代变量color直接表示一个元素
for (let color of colors)
console.log(color)
- break(常用):终止循环
- continue:跳过本次循环后面的语句,进入下一次循环
在 for…of 循环中,可迭代对象可以是数组、字符串、Map、Set 等,但不能是对象,对象是不可枚举的类型。如果需要遍历对象中的属性,可以使用 for…in 循环。
练习
求两个函数的最大值
function max(a,b) {
return a > b ? a : b;
}
console.log(max(2.5,2));
完成函数
function isLandscape(width, height) {
return width > height;
}
console.log(isLandscape(2,1));
FizzBuzz
// 可被3除以的 => Fizz
// 可被5整除 => Buzz
// 可被3和5整除 => FizzBuzz
// 不能被3或5整除 => 输入
// 不是一个数字 => "不是一个数字
console.log(fizzBuzz(16));
function fizzBuzz(input) {
if (typeof input !=='number')
return NaN;
if ((input % 3 === 0) && (input % 5 === 0))
return 'FizzBuzz';
if (input % 3 === 0)
return 'Fizz';
if (input % 5 === 0)
return 'Buzz';
return input;
}
完成速度函数,每超过5公里就给司机扣一分
// Speed Limit = 70
// 5 -> 1 point
// 12 points -> suspended
// 假设限速70公里,如果车子行驶在限速内,显示'OK'
// 超过70公里后,每5公里就得到一个点,如 75 公里显示 1个点,72公里显示 'OK'
// 如果被扣12分,驾照就被吊销了
checkSpeed(130);
// function checkSpeed(speed){
// let count = Math.floor((speed - 70) / 5);//<70的时候用不着计算count的
// if (count >= 12) console.log('suspend');
// else if (speed <= 74) console.log('ok'); //不要在程序中使用神秘数字,如70,使用常量或者变量
// else console.log('point:' + count);
// }
function checkSpeed(speed){
const speedLimit = 70;
const kmPerPoint = 5;
if( speed < speedLimit + kmPerPoint){
console.log('ok');
return; //直接终止函数,后面的代码都不会运行
}
// else{ 使用else产生了缩进,使得需要左右滚动才能看全,为了简洁 ->改进
const points = Math.floor((speed - speedLimit) / kmPerPoint);//使用常量,防止意外地修改了这个值(修改时会报错)
if (points >= 12)
console.log('License suspended');
else
console.log('Points', points);
// }
}
for循环显示奇偶数
showNumbers(10);
function showNumbers(limit) {
for (let i = 0; i <= limit; i++) {
const message = (i%2 === 0) ? 'EVEN' : 'ODD';
console.log(i,message);
}
}
输入一个数组,计数类真值Truthy
const array = [0, null, undefined, '', 2, 3];
console.log(countTruthy(array));
function countTruthy(array) {
let count = 0;
for (let item of array) {
if (item) count++;
}
return count;
}
显示对象当中的所有string 属性
const movie = {
title: 'a',
releadeYear: 2008,
rating: 4.5,
director: 'b'
};
showProperties(movie);
function showProperties(obj) {
for (let key in obj)
if (typeof(obj[key]) === 'string') console.log(key,obj[key]);
}
返回limit之内的3和5的倍数的数字的和
console.log(sum(10));
function sum(limit) {
let sum = 0;
for (let i = 1; i <= limit; i++) {
if ( (i%3 === 0) || (i%5 === 0) ) sum += i;
}
return sum;
}
计算分数的平均值,并输出不同分数段的等级
const marks = [80, 80, 100];
console.log(calculateGrade(marks));
function calculateAverage(array) {
let sum = 0;
for (let value of array) {
sum += value;
}
return sum / array.length;
}
function calculateGrade(marks) {
const average = calculateAverage(marks);
if (average < 60) return 'F';
if (average < 70) return 'D';
if (average < 80) return 'C';
if (average < 90) return 'B';
return 'A';
}
每行输出与行数相等的‘*’
showStarts(10)
function showStarts(rows) {
for (let row = 1; row <= rows; row++) {
let pattern = '';
for (let i = 0; i < row; i++)
pattern +='*';
console.log(pattern)
}
}
求出limit之内的所有质数
showPrimes(10);
function showPrimes(limit){
for(let number = 2; number <= limit; number++)
if(isPrime(number)) console.log(number);
}
function isPrime(number){
for(let factor = 2; factor < number; factor++)
if(number % factor === 0)
return false;//找到了第一个因数直接返回false
return true; //没有找到任何因数
}
Object
基础
对象就是键值对的集合,如果有高度相关的属性,就将它们封装到对象中
// Object-oriented Programming (OOP)
const circle = {
radius: 1,
location: {
x: 1,
y: 1
},
isVisible: true,
draw: function() {
console.log('draw');
}
};
circle.draw(); // Method
工厂函数
工厂函数可以创建对象
// Factory Function
function createCircle(radius) {
return {
//在新版js中, 如果键和值一样,可以把值删掉
radius, // 等价于 radius : radius
draw() {
console.log('draw')
}
}
}
const circle1 = createCircle(1);
console.log(circle1);
const circle2 = createCircle(1);
console.log(circle2);
构造函数
驼峰命名:第一个单词首字母小写,之后的单词首字母大写
帕斯卡命名:所有的单词首字母都大写
// Constructor Function
// 构造函数通常采用帕斯卡命名规则
function Circle(radius) {
// this 可以视为是一个空对象
this.radius = radius;
this.draw = function () {
console.log('draw');
}
}
const circle = new Circle(1);
// new 的作用
// 创建了一个空对象 (此处的circle)
// 将this指向这个空对象
// 返回创造出的空对象
对象的动态特性
JavaScript对象是动态的,创建之后可以添加属性和方法或删除已有成员
const circle = {
radius: 1
};
// 此处的const指不能将circle指定为新的对象
// 但可以通过添加或删除成员来修改它指向的对象的值
circle.color = 'yellow';
circle.draw = function() {}
delete circle.draw;
console.log(circle);
构造器属性
const x = {};
const y = new String();
// 每个对象都有一个构造器属性,引用创建该对象的构造函数
console.log(x.constructor);
console.log(y.constructor);
函数即对象
在 JavaScript 中,函数也是对象。它们是可以被创建,赋值给变量,传递给其他函数作为参数,并且可以有属性和方法。
在JavaScript中,函数拥有对象所拥有的全部功能。
function Circle(radius) {
this.radius = radius;
this.draw = function () {
console.log('draw');
}
}
// 等价于
const circle = new Function('radius',`
this.radius = radius;
this.draw = function () {
console.log('draw');}
`);
// call 用这个方法,调用一个函数。
// 第一个参数是this,this会指向我们传入的参数。第一个参数后面的是实参
// Circle.call({},1); 等价于 new Circle(1);
// Circle.call({},1,2,3,4);
Circle.call({},1);
//apply 不用一个个传入实参,传入实参数组
Circle1.apply({},[4]);
// new操作符在内部创建一个{},并将它作为实参传入call方法中,然后这个空对象就会指定给this,this就完成了对空对象的引用
const another = new Circle(1);
值类型与引用类型
值类型(value types)数据
- String
- Number
- Boolean
- undefined
- null
- Symbol(ES6新增)
引用类型(reference types)数据
- Object
- Array
- Function
注意:
- 声明值类型的变量时,变量的值保存在变量中
- 声明引用类型的变量,变量中保存的是对象实体的内存地址
结论:
- 值类型的数据复制时复制值
- 引用类型的数据或对象复制引用(地址)
let x = {value: 10};
let y = x;
x.value = 20;
let number = 10;
function increase1(number) {
number++;
}
increase1(number);
console.log(number);
let number = 10;
function increase1(number) {
number++;
}
increase1(number);
console.log(number);
枚举一个对象的属性
-
fo…in
最简单
-
for…of
在 for…of 循环中,可迭代对象可以是数组、字符串、Map、Set 等,但不能是对象,对象是不可枚举的类型。如果需要遍历对象中的属性,可以使用 for…in 循环。
-
Object.keys()
这个方法获得输入对象的所有成员的键,并返回一个数组。因为数组是可枚举的,所有我们可以用for…of
-
Object.entries()
这个方法不是返回一个键名组成的字符串,而是返回由键值对组成的数组
-
in操作符
使用in操作符,可以检查对象中是否包含给定的键
const circle = {
radius: 1,
draw() {
console.log('draw');
}
};
for (let key in circle)
console.log(key, circle[key]);
for (let key of Object.keys(circle))
console.log(key); //这里会报错,因为circle不可枚举
for (let entry of Object.entries(circle))
console.log(entry);
if ('radius' in circle)
console.log('yes');
对象克隆
- Object.assign方法将一个或多个源对象的成员复制到目标对象中,可以用来克隆对象,或将多个对象整合为1个
- 拆分操作符可以将对象拆解,即读取所有的对象成员,然后将它们全部复制到新的对象中
const circle = {
radius: 1,
draw() {
console.log('draw');
}
};
// 将一个或多个源对象的成员复制到目标对象中
// 或将多个对象整合为一个
const c1 = Object.assign({
color: 'yellow'
}, circle);
// ...读取所有的对象成员,然后将它们全部复制到新的对象中
const c2 = {...circle};
console.log(c1);
console.log(c2);
垃圾回收
在低级的计算语言,当创建对象时,我们需要分配内存,当对象使用完毕时,我们需要回收分配的内存,JavaScript中不需要,因为有垃圾回收机制
let circle = {}
当我们创建对象时,内存会自动分配给这个对象。用完之后不用销毁回收内存。
垃圾回收机制的工作,就是找出不再使用的变量和常量,然后回收分配给他们的内存。
内存的分配和回收是内部进行的
Math
-
Math.random()
返回一个0到1之间的随机数
-
Math.round()
-
Math.max()
传入多个数,返回最大的数
-
Math.min()
传入多个数,返回最小的数
const random = Math.random();
const round = Math.round(1.9);
const max = Math.max(1, 2, 3, 4, 5);
const min = Math.min(1, 2, 3, 4, 5);
String
-
[序号]
访问指定位置的字符,使用[序号],如message[1]
-
includes(字符串)
判断字符串中是否包含某个词,使用includes。如:message.includes(‘my’)
-
startWith
判断字符串是否以某个字符串开头。如message.startWith(‘This’)
-
endWith
判断字符串是否以某个字符串结尾。如message.endWith(‘e’)
-
indexOf
获取某个字符串在字符串中的序号。如message.indexOf(‘my’)
-
replace
替换字符串的内控。如message.replace(被替换的字符串,新字符串)
-
toUpperCase
返回全是大写的字符串。如message.toUpperCase()
-
toLowerCase
返回全是小写的字符串。如message.toLowerCase()
-
trim
去掉字符串头尾的空格
- trimLeft 去掉左边的空格
- trimRight 去掉右边的空格
-
split
-
转义字符
转义序列 Unicode 代码流 \0
null 字符(U+0000 NULL) \'
单引号(U+0027 APOSTROPHE) \"
双引号(U+0022 QUOTATION MARK) \\
反斜杠(U+005C REVERSE SOLIDUS) \n
换行符(U+000A LINE FEED; LF) \r
回车符(U+000D CARRIAGE RETURN; CR) \v
垂直制表符(U+000B LINE TABULATION) \t
制表符(U+0009 CHARACTER TABULATION) \b
退格键(U+0008 BACKSPACE) \f
换页符(U+000C FORM FEED) \uXXXX
…其中XXXX
恰好是0000
–FFFF
范围内的 4 个十六进制;例如,\u000A
与\n
(换行)相同;\u0021
是!
U+0000
和U+FFFF
之间的 Unicode 码位(Unicode 基于多平台语言)\u{X}
…\u{XXXXXX}
…其中X
…XXXXXX
是0
–10FFFF
范围内的 1-6 个十六进制数字;例如,\u{A}
与\n
(换行符)相同;\u{21}
是!
U+0000
和U+10FFFF
之间的 Unicode 码位(整个 Unicode)\xXX
…其中XX
恰好是00
–FF
范围内的 2 个十六进制数字;例如,\x0A
与\n
(换行符)相同;\x21
是!
U+0000
和U+00FF
之间的 Unicode 码位 (Basic Latin 和 Latin-1 Supplement 块;相当于 ISO-8859-1)
// String primitive
const message = 'This is my first message';
// String object
const another = new String('hi');
模板语法
- 所见即所得,不用使用转义字符
- 可以使用占位符,动态生成内容
let name = 'John';
const s =
`Hi ${name},
Thank you for ...
Regards,
Mosh`;
console.log(s);
Date
const now = new Date();
const date1 = new Date('May 11 2018 09:00');
const date2 = new Date(2018, 4, 11, 9);
console.log(now.toDateString()); //Fri Jun 30 2023
console.log(now.toTimeString());//11:23:15 GMT+0800
console.log(now.toISOString());//2023-06-30T03:23:15.758Z T前是日期T后是实际,这是ISO格式,通常用于网络服务
练习
遍历对象
创建一个address对象,该对象有street、city、zipCode 三个属性,创建一个showAddress函数,用于显示这个对象所有的属性和对应的值
const address = {
street: 'a',
city: 'b',
zipCode: 'c'
}
function showAddress(address){
for (const key in address)
console.log(key, address[key])
}
showAddress(address);
用工厂函数和构造函数初始化对象
// Factory function
function createAddress(street,city,zipCode) {
return {
street,
city,
zipCode
}
}
const address = createAddress('a', 'b', 'c');
console.log('address',address);
// Constructor function
function Address(street,city,zipCode) {
this.street = street;
this.city = city;
this.zipCode = zipCode;
}
const another = new Address('a', 'b', 'c');
console.log('another',another);
对象相等
创建函数areEqual检测两个对象是否相等,如果这两个对象每个属性都相等,返回true否则返回false
创建函数areSame检测两个对象是不是指向同一个对象,即它们的引用是一样的
let address1 = new Address('a', 'b', 'c');
let address2 = new Address('a', 'b', 'c');
let address3 = address1;
console.log(areEqual(address1,address2))
console.log(areSame(address1,address2))
console.log(areSame(address1,address3))
// Constructor function
function Address(street,city,zipCode) {
this.street = street;
this.city = city;
this.zipCode = zipCode;
}
function areEqual(address1, address2) {
return address1.street === address2.street &&
address1.city === address2.city &&
address1.zipCode === address2.zipCode;
}
function areSame(address1, address2) {
return address1 === address2
}
我的思路
let address1 = new Address('a', 'b', 'c');
let address2 = new Address('a', 'b', 'c');
let address3 = address1;
console.log(areEqual(address1,address2))
console.log(areSame(address1,address2))
console.log(areSame(address1,address3))
// Constructor function
function Address(street,city,zipCode) {
this.street = street;
this.city = city;
this.zipCode = zipCode;
}
function areEqual(address1, address2) {
if(areSame(address1,address2) )return true;
if(Object.keys(address1).length !== Object.keys(address2).length) return false;
for (let key in address1){
if(!(key+'' in address2)) return false;
if(address1[key] !== address2[key]) return false;
}
return true
}
function areSame(address1, address2) {
return address1 === address2
}
博客文章对象
创建一个博客对象,给每个属性一个值
title(标题)、body(文章)、author(作者)、views(阅读量)、comments(评论数组)(author,body)、isLive(点赞的属性,Boolean类型)
post = {
title: 'a',
body: 'b',
author: 'c',
views: 10,
comments: [
{author: 'a', body: 'b'},
{author: 'c', body: 'd'}
],
isLive: true
};
console.log(post);
构造函数
假设要建立一个博客管理器,用户打了个草稿,但是还没有正式发布
function Post(title, body, author) {
this.title = title;
this.body =body;
this.author = author;
this.views = 0;
this.comments = [];
this.isLive = false;
}
const post = new Post('a','b','c');
console.log(post);
价格范围对象
let priceRange = [
{label: '$', tooltip: 'Inexpensive', minPerPerson: 0, maxPerPerson: 10 },
{label: '$$', tooltip: 'Moderate', minPerPerson: 11, maxPerPerson: 20 },
{label: '$$$', tooltip: 'Expensive', minPerPerson: 21, maxPerPerson: 50 }
];
let restaurants = [
{averagePerPerson: 5}
];
Array
元素的操作
- 添加
- 查找
- 删除
- 拆分
- 合并
添加元素
push()
方法将一个或多个元素添加到数组的末尾,并返回该数组的新长度unshift()
方法将一个或多个元素添加到数组的开头,并返回该数组的新长度。splice()
方法通过删除或替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内容。此方法会改变原数组。
// 3种添加数组元素的方法
// 加到末尾
// 加到开头
// 加到中间指定位置
// End
numbers.push(5,6);//push方法可以输入1个或多个参数,这些参数的值添加到数组的末尾
// Beginging
numbers.unshift(1,2);//unshift将参数添加到数组前,也可输入多个参数
// Middle
numbers.splice(2, 0, 'a', 'b');//splice可以向指定位置添加或删除元素,第一参数是起始位置,第二个参数是要删除的元素的个数
console.log(numbers);
查找元素
值类型和引用类型查找元素是不同的
查找元素(值类型)
indexOf()
方法返回可以在数组中找到给定元素的第一个索引,如果不存在,则返回 -1。lastIndexOf()
查找数组中元素最后一次出现的索引,如未找到返回-1。includes()
方法用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回 true,否则返回false。
const numbers = [1, 2, 3, 1, 4]
// indexOf 方法返回值的序号,没有返回-1 (要匹配的元素,从第几个元素开始匹配)
console.log(numbers.indexOf(1, 3));
// lastIndexOf 方法返回最后一个匹配的结果,没有返回-1
console.log(numbers.lastIndexOf(1));
// 判断数组中是否存在某个值
console.log(numbers.indexOf(1) !== -1);
console.log(numbers.includes(1));
查找元素(引用类型)
find()
返回满足特定条件的元素对象或者元素值, 不满足返回undefined
findIndex()
返回数组中符合条件的第一个元素的索引,没有,则返回-1
。
// find 方法返回找到的第一个匹配的对象
const courses = [
{ id: 1, name: 'a'},
{ id: 2, name: 'b'},
];
// findIndex 方法返回找到的第一个匹配的对象
// const course = courses.find(function(course){
// return course.name === 'a';
// });
// findIndex 方法返回找到的第一个匹配的对象的序号
const course = courses.findIndex(function(course){
return course.name === 'a';
});
console.log(course);
箭头函数
const courses = [
{ id: 1, name: 'a'},
{ id: 2, name: 'b'},
];
// const course = courses.find(function(course){
// return course.name === 'a';
// });
// 箭头函数
// 一个参数,可以把括号去掉;没有参数,就用()
const course = courses.find(course => course.name === 'a');
删除元素
pop()
方法从数组中删除最后一个元素,并返回该元素的值。此方法更改数组的长度。shift()
方法从数组中删除第一个元素,并返回该元素的值。此方法更改数组的长度。splice()
方法通过删除或替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内容。此方法会改变原数组。
const numbers = [1, 2, 3, 4];
// End
// pop 方法删除最后一个元素,并且作为结果返回
// const last = numbers.pop();
// console.log(numbers);
// console.log(last);
// shift 方法删除开头的元素,并且作为结果返回
// const last = numbers.shift();
// console.log(numbers);
// console.log(last);
// splice(元素的序号,从序号开始要删除几个元素)
numbers.splice(2, 2);
console.log(numbers)
清空数组
let numbers = [1, 2, 3, 4];
let another = numbers;
// 方案1:给数组重新赋值
// 给数组重新赋值时,numbers指向内存中一个新的数组,another还指向旧的对象
// numbers = [];
// 方案2:直接将数组的length值设为0
// 这样会强制清空数组,删掉所有的元素
// numbers : [] another: []
// numbers.length = 0;
// 方案3:splice
// numbers.splice(0, numbers.length);
// 方案4:pop(不提倡)
while (numbers.length > 0)
numbers.pop();
console.log(numbers);
console.log(another);
合并和切割数组
concat()
方法用于合并两个或多个数组。slice()
方法返回一个新的数组对象,这一对象是一个由begin
和end
决定的原数组的浅拷贝(包括begin
,不包括end
)。原始数组不会被改变。
const first = [1, 2, 3];
const second = [4, 5, 6];
// 合并数组 concat 方法用于合并数组,两个原始数组不会受影响,这个方法返回一个组合后的新副本
const combined =first.concat(second);
// 切割数组
// 第1种:传入2个代表首尾的序号
// const slice = combined.slice(2,4);
// 第2种: 去掉第二个参数 得到序号之后的另一个数组
// const slice = combined.slice(2);
// 第3种: 没有参数 得到原数组的备份副本
const slice = combined.slice();
console.log(combined);
console.log(slice);
// 上面两种方法处理的都是值类型,如果数组中的元素是对象,对象不会复制,复制的是它们的引用,
// 意味着,输出的新数组中对应元素只想的是之前的同一对象。切割也是如此
拆分操作符
const first = [1, 2, 3];
const second = [4, 5, 6];
// const combined = first.concat(second);
const combined = [...first,'a',...second,'b'];
// 复制数组所有的成员
// const copy = combined.slice();
const copy = [...combined];
迭代数组
const numbers = [1, 2, 3];
for (const number of numbers)
console.log(number);
numbers.forEach(function(number){
console.log(number);
})
// 简化
numbers.forEach(number => console.log(number));
// 回调函数可以有第2个参数index(序号)
numbers.forEach((number,index) => console.log(index,number));
连接数组
join()
方法通过连接数组元素用逗号或指定的分隔符字符串分隔,返回一个字符串。
const numbers = [1, 2, 3];
// join 方法返回一个字符串 参数可选 可以将数组转字符串
const joined = numbers.join(',');
console.log(joined);
// split 方法可以将字符串转数组
const message = 'This is my first message';
const parts = message.split(' ');
console.log(parts);
const combined = parts.join('-');
console.log(combined);
数组排序
-
sort() 方法采用 原地算法进行排序并返回数组。默认排序顺序是在将元素转换为字符串,然后比较它们的UTF-16代码单元值序列
原地算法是一个使用辅助的数据结构对输入进行转换的算法。但是,它允许有少量额外的存储空间来储存辅助变量。当算法运行时,输入通常会被输出覆盖。原地算法仅通过替换或交换元素来更新输入序列。
-
reverse()
方法将数组中元素的位置颠倒,并返回该数组。数组的第一个元素会变成最后一个,数组的最后一个元素变成第一个。该方法会改变原数组。
const numbers = [2, 3, 1];
// sort 方法用于数组排序 sort方法将数组的每个元素转换为字符串,再对字符串进行排序
numbers.sort();
console.log(numbers);
// 方法可以将数组元素的顺序颠倒
numbers.reverse();
console.log(numbers);
// 当操作的数组里的元素是字符串或数字时,sort方法可以起作用,但当操作的元素是对象时,默认不起作用,要使用回调函数处理
const courses = [
{ id:1, name: 'NOde.js'},
{ id:2, name: 'javaScript'},
];
courses.sort(function(a,b) {
// a < b => -1
// a > b => 1
// a === b => 0
// if(a.name > b.name) return -1;
// if(a.name < b.name) return 1;
// 因为大小写字母在Askii表中序号不一样,所以要进行大小写转换
const nameA = a.name.toLowerCase();
const nameB = b.name.toLowerCase();
if (nameA < nameB) return -1;
if (nameA > nameB) return 1;
return 0;
});
console.log(courses);
检测数组中的元素
every
用来判断数组内所有元素是否符合某个条件,返回 布尔值some()
用来判断数组元素是否符合某个条件,只要有一个元素符合,那么返回true
const numbers = [1, -2, 2, 4];
// every、some新版js中才有的方法
// every 作用:检测每个元素是否符合条件 方法返回Bolearn类型 遍历数组,将每个元素输入回调函数,找到不符合条件的就停止
const allPositive = numbers.every(function(value) {
return value >= 0;
});
console.log(allPositive);
// some 作用:检查是否至少一个元素符合条件 some也会将每个元素输入回调函数,找到一个满足条件的元素就停止
const atLeastOnePositive = numbers.some(function(value) {
return value >= 0;
});
console.log(atLeastOnePositive);
筛选数组
filter()
用来遍历原数组,过滤拿到符合条件的数组元素,形成新的数组元素。
// filter 方法会遍历数组,将每个元素传入回调函数,如果元素满足条件,会将元素添加到新的数组中
const numbers = [1, -1, 2, 3];
// const filtered = numbers.filter(function(value){
// return value >=0;
// })
const filtered = numbers.filter(value >=0);
数组映射
map()
创建一个新的数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。
// map方法可以将元素映射为其他东西,可以映射为字符串,也可以映射为对象等
const numbers = [1, -1, 2, 3];
const filtered = numbers.filter(n => n >=0);
// const items = filtered.map(n => '<li>' + n + '</li>');
// const html = '<ul>' + items.join('') + '</ul>';
// console.log(html);
// 将每个元素映射为新对象
// const items = filtered.map(n =>{
// const obj = { value: n};
// return obj;
// });
// const items = filtered.map(n => { value: n}); //JavaScript解读时,会认为这是代码块,而不是返回对象,如果要返回对象,要包在()里
// const items = filtered.map(n => ({ value: n}));
// console.log(items);
// filter和map都是返回新数组,不会对原数组产生影响,这些方法都是可以链接的,即可以像链条一样调用
const items = numbers
.filter(n => n >= 0)
.map(n => ({value: n}))
.filter(obj => obj.value > 1)
.map(obj => obj.value);
console.log(items);
汇总数组
reduce()
数组元素累计器,返回一个合并的结果值。
const numbers = [1, -1, 2, 3];
// let sum = 0;
// for (let n of numbers)
// sum +=n;
// 在reduce方法后,以1个初始累计值开始,循环遍历整个数组,然后将所有元素合并为一个值,这里得到所有元素的累加之后
// const sum = numbers.reduce((accumulator, currentValue) => {
// return accumulator + currentValue;
// },0)
// 如果保持默认,第1个元素就会被当做累计值的初始值
const sum = numbers.reduce(
(accumulator, currentValue) => accumulator + currentValue
);
console.log(sum);
练习
Array from Range
const numbers = arrayFromRange(-10, -4);
console.log(numbers);
function arrayFromRange(min, max) {
const output = [];
for (let i = min; i <= max; i++)
output.push(i);
return output;
}
Includes
数组是否包含某元素
const numbers = [1, 2, 3, 4];
console.log(includes(numbers,-1));
function includes(array, searchElement){
for (const element of array) {
if(element === searchElement) return true;
}
return false;
}
Except
数组删除后组成新数组
const numbers = [1, 2, 3, 4, 1, 1];
const output = except(numbers, [1, 2]);
console.log(output);
function except(array, excluded) {
const output = [];
for (let element of array) {
if (!excluded.includes(element))
output.push(element);
}
return output;
}
Moving an Element
将元素移动到指定位置
const numbers = [1, 2, 3, 4];
const output = move(numbers, 0, 0);
console.log(output);
function move(array, index, offset) {
const position = index + offset;
if(position > array.length || position < 0){
console.error('Invalid offset.');
return;
}
const output = [...array];
const element = output.splice(index, 1)[0];
output.splice(position, 0 , element);
return output;
}
Count Occurrences
元素在数组中出现的次数
const numbers = [1, 2, 3, 4, 1];
const count = countOccurrences(numbers, 1);
console.log(count);
function countOccurrences(array, searchElement){
let count = 0;
// for (const element of array)
// if(element === searchElement) count+=1;
// return count;
return array.reduce((accumulator, current) =>{
const occurrence = (current === searchElement) ? 1 : 0;
console.log(accumulator, current, searchElement);
return accumulator + occurrence;
})
}
Get Max
const numbers = [1, 2, 3, 4, 1];
console.log(getMax(numbers));
function getMax(array) {
// let max = array[0];
// for (let value of array) {
// if (value > max) max = value;
// }
// return max;
return array.reduce((a, b) => (a > b) ? a : b);
}
Movies
const movies = [
{ title: 'a', year: 2018, rating: 4.5},
{ title: 'b', year: 2018, rating: 4.7},
{ title: 'c', year: 2018, rating: 3},
{ title: 'd', year: 2017, rating: 4.5},
];
const res = movies
.filter(m => m.year === 2018 && m.rating > 4)
.sort((a,b) => a.rating - b.rating)
.reverse()
.map(m => m.title);
console.log(res);
Function
函数声明与表达式
-
函数声明
function walk() { console.log('walk'); }
-
函数表达式
let run = function() { console.log('run') };
Hoisting(提前)
Hoisting 是将函数的声明提前到代码顶端的过程 (系统后台自动进行),因此我们可以在函数声明之前就使用这个函数
// 函数声明可以在函数声明前调用
walk();
function walk() {
console.log('walk');
}
// 函数表达式不可以在声明前调用,这里会报错
run();
const run = function() {
console.log('run');
}
Arguments
注:for…of 可以用于循环各种有迭代器的对象,如arguments有一个Symbol.iterator的属性
function sum() {
let total = 0;
for (let value of arguments)
total += value;
return total;
}
console.log(sum(1, 2, 3, 4, 5, 10));
剩余操作符
拆分在数组的含义就是获得每个单独的元素,在函数的参数使用3个点时,就是剩余操作符
在函数的参数前使用剩余操作符,它会将输入给函数的所有参数转到一个数组中
// function sum(...args) {
// return args.reduce((a, b) => a + b);
// }
// console.log(sum(1, 2, 3, 4, 5, 10));
// 利用上面的函数计算购物车总消费
function sum(discount, ...args) {
const total = args.reduce((a, b) => a + b);
return total * (1 - discount);
}
console.log(sum(0.1, 20, 30));
注:剩余参数必须是函数的最后一个参数
默认参数值
最佳实践:当给某个参数默认值时,确保这个参数是所有参数的最后一个,或者其后面位置的参数都有默认值
// 最佳实践:当给某个参数默认值时,确保这个参数是所有参数的最后一个,或者其后面位置的参数都有默认值
function interest (principal, rate = 3.5, years) {
// 可以用逻辑或运算符来给变量设置默认值
// rate = rate || 3.5;
// years = years || 5;
return principal * rate / 100 * years;
}
console.log(interest(1000,undefined,5));
Getters and Setters
getters 用来获取对象的属性,setters用了修改对象的属性
const person = {
firstName: "Mosh",
lastName: "Hamedani",
fullName() {
return `${person.firstName} ${person.lastName}`
},
};
console.log(person.fullName());
上述代码可以改进:1.fullName是只读 2.不想通过函数的方式调用
const person = {
firstName: "Mosh",
lastName: "Hamedani",
// getters => access properties
get fullName() {
return `${person.firstName} ${person.lastName}`
},
// setters => change them
set fullName(value) {
const parts = value.split(' ');
this.firstName = parts[0];
this.lastName = parts[1];
}
};
person.fullName = 'John Sm';
console.log(person.fullName);
Try and Catch
防御式编程:在函数或者方法开始使用错误处理
抛出一个异常后,throw后面的代码都不会执行,控制器会跳出方法,并且直接移动到catch块中
const person = {
firstName: "Mosh",
lastName: "Hamedani",
// getters => access properties
get fullName() {
return `${person.firstName} ${person.lastName}`
},
// setters => change them
set fullName(value) {
if (typeof value !== 'string')
throw new Error('Value is not a string.');
const parts = value.split(' ');
if (parts.length !== 2)
throw new Error('Enter a first and last name.');
this.firstName = parts[0];
this.lastName = parts[1];
}
};
try {
person.fullName = undefined;
}
catch (e) {
alert(e);
}
console.log(person.fullName);
局部与全局作用域
- 变量或常量的作用域,决定它们可以在什么地方访问
- let和const定义的变量的作用域是定义它们的代码块
- 局部变量或常量会自动覆盖同名的全局变量或常量
const color = 'red';
function start() {
const message = 'hi';
const color = 'blue';
console.log(color);
}
function stop() {
const message = 'bye';
}
start();
Let 与 Var
let和const的作用域是代码块,var的作用域是函数作用域
// var 的作用域不是它被定义的代码块,而是它所属的函数的作用域
function start() {
for (var i = 0; i < 5; i++)
console.log(i);
console.log(i);
}
let color = 'red'; //这个变量将被添加到windows
let age = 30;
function sayHi() {
console.log('hi');
}
总结:var、let、const的区别
1、var定义的变量,没有块的概念,可以跨块访问, 不能跨函数访问。
2、let定义的变量,只能在块作用域里访问,不能跨块访问,也不能跨函数访问。
3、const用来定义常量,使用时必须初始化(即必须赋值),只能在块作用域里访问,而且不能修改。
4、同一个变量只能使用一种方式声明,不然会报错
This关键字
什么是this:this引用了执行当前函数的对象实例
- 如果一个函数是对象中的方法,this指向这个对象本身
- 如果一个函数是常规函数,或者它不是某个对象的一部分,this就指向全局对象(也就是浏览器中的window,或node中的global)。但如果用new操作符来调用一个构造函数时,this将会绑定到一个新的空对象上
// method -> obj
// function -> global (window, global)
const video = {
title: 'a',
play() {
console.log(this);// this指向video对象自己
}
}
video.stop = function() {
console.log(this);//this指向video对象自己,因为stop是video对象的内部方法
}
video.play();
function playVideo(){
console.log(this);//this指向全局对象。也就是浏览器中的window,或node中的global
}
function Video(title) {
this.title = title;
console.log(this);
}
const v = new Video('b');//this指向new关键字创建的新对象
// // 注:使用new关键字时,会创建一个新的空对象,并将构造函数中的this绑定到这个空对象上
const video = {
title: 'a',
tags: ['a', 'b', 'c'],
showTags() {
// this.tags.forEach(function(tag){
// console.log(this, tag);//回调函数是常规函数,this指向全局对象,是全局兑现执行了这个匿名函数
// })
this.tags.forEach(function(tag){
console.log(this.title, tag);
},this);//thisArg:第2个参数是this参数,可以输入一个对象,然后这个回调函数的this就会绑定那个对象
}
}
video.showTags();
改变this的值
如何改变一个函数的this指向
-
声明一个新的常量self指向当前的对象
const video = { title: 'a', tags: ['a', 'b', 'c'], showTags() { const self = this; this.tags.forEach(function(tag){ console.log(self.title, tag); }); } } video.showTags();
-
通过apply,bind,call
- apply,bind和call都可以改变函数this的值
- call 和apply的区别在于如何输入实参
- bind函数不会调用函数,它会返回一个新函数,将新函数的this绑定到输入的对象上
function playVideo() { console.log(this); } playVideo.call({ name: 'Mosh' }); playVideo.apply({ name: 'Mosh' }); playVideo();
function playVideo(a, b) { console.log(this); } playVideo.call({ name: 'Mosh' }, 1, 2); playVideo.apply({ name: 'Mosh' }, [1, 2]); const fn = playVideo.bind({ name: 'Mosh' });//bind函数不会调用playVideo函数,它会返回一个新函数,将新函数的this绑定到输入的对象上 fn(); playVideo.bind({ name: 'Mosh' })();//也可以直接调用 playVideo(); const video = { title: 'a', tags: ['a', 'b', 'c'], showTags() { this.tags.forEach(function(tag){ console.log(this.title, tag); }.bind(this)); } }
-
使用箭头函数
- 箭头函数的好处之一就是:它继承了this的值
const video = {
title: 'a',
tags: ['a', 'b', 'c'],
showTags() {
// 箭头函数从它的容器函数中继承了this的值
this.tags.forEach(tag => {
console.log(this.title, tag);
});
}
}
练习
Sum of Arguments
console.log(sum([1,2,3,4]));
function sum(...item) {
if (item.length === 1 && Array.isArray(item[0]))
item = [...item[0]];
return item.reduce((a,c) => a+c);
}
Area of Circle
const circle = {
radius: 1,
get area() {
return Math.PI * this.radius * this.radius;
}
};
circle.radius = 2;
console.log(circle.area);
Error Handling
const numbers = [1, 2, 3, 4, 1];
try {
console.log(countOccurrences('numbers', 1));
}
catch (e) {
console.log(e.message);
}
function countOccurrences(array, searchElement) {
if (!Array.isArray(array))
throw new Error('Not a array.');
return array.reduce( (a,c) => a + (c === searchElement),0 );
}