JavaScript 基础篇 - Mosh

本文介绍了JavaScript中的变量命名规则、数据类型(值类型和引用类型)、对象、函数、数组操作、字符串处理、日期操作等基础知识,还包括流程控制、错误处理和类型检查等内容。
摘要由CSDN通过智能技术生成

basics

变量

  1. 命名:不能是关键字、要有意义、不能以数字开头、不能包含空格和减号(多个单词拼在一起firstName)、大小写敏感

  2. 一行定义一个变量

  3. 没有初始化的变量类型: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);

枚举一个对象的属性

  1. fo…in

    最简单

  2. for…of

    在 for…of 循环中,可迭代对象可以是数组、字符串、Map、Set 等,但不能是对象,对象是不可枚举的类型。如果需要遍历对象中的属性,可以使用 for…in 循环。

  3. Object.keys()

    这个方法获得输入对象的所有成员的键,并返回一个数组。因为数组是可枚举的,所有我们可以用for…of

  4. Object.entries()

    这个方法不是返回一个键名组成的字符串,而是返回由键值对组成的数组

  5. 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 代码流
    \0null 字符(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 恰好是 0000FFFF 范围内的 4 个十六进制;例如,\u000A\n(换行)相同;\u0021!U+0000U+FFFF 之间的 Unicode 码位(Unicode 基于多平台语言)
    \u{X}\u{XXXXXX} …其中 XXXXXXX010FFFF 范围内的 1-6 个十六进制数字;例如,\u{A}\n(换行符)相同; \u{21}!U+0000U+10FFFF 之间的 Unicode 码位(整个 Unicode)
    \xXX …其中 XX 恰好是 00FF 范围内的 2 个十六进制数字;例如,\x0A\n(换行符)相同;\x21!U+0000U+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() 方法返回一个新的数组对象,这一对象是一个由 beginend 决定的原数组的浅拷贝(包括 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);

局部与全局作用域

  1. 变量或常量的作用域,决定它们可以在什么地方访问
  2. let和const定义的变量的作用域是定义它们的代码块
  3. 局部变量或常量会自动覆盖同名的全局变量或常量
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指向

  1. 声明一个新的常量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();
    
  2. 通过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));
        }
    }
    
  3. 使用箭头函数

    • 箭头函数的好处之一就是:它继承了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 );
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值