分享一篇JS笔记(保姆级)

109 阅读15分钟

【前言】JS是前端开发中最重要的一门语言,其中数组、函数、BOM、DOM的操作在日常开发中尤为重要,这篇文章主要整理总结了JS的作用域、函数、数组等常用知识点。

作用域

块级作用域

特点:

  1. {}里面有let关键字或者const关键字;
  2. 在块级作用域中定义的变量只能在块级作用域中使用。

const关键字

  • 作用:定义常量(值是固定的不能修改);
  • 特点: 定义常量必须设置默认值, 常量的值不能被修改, 必须先定义后使用。

作用域链

定义:多个作用域嵌套形成的链状结构;

  • 作用: 在程序中,通过作用域链查找变量并执行;
  • 特点: 先从程序执行的当前作用域中查找,如果没有找到,沿着作用域链向上一级查找

函数

递归函数

定义:在函数内部,函数自己调用自己。

特点:递归函数默认就是一个死循环

代码实现

function fn() {
    fn();
    //递归函数停止
    return
}
fn();

闭包函数

定义:在函数内部,其他函数使用了当前函数中的变量,整体函数就是闭包函数 代码实现

function fn() {
        let a = 123;
     function fn1() {
       a++;
           }
       fn1();
           }
   fn();

自调用函数

本质:函数调用的一种方式。

语法:(函数体)();

函数参数

形参默认值

定义:函数中形参本身就是一个变量,所以给变量设置默认值;

语法function fn(a = 123, b = 456) {}

动态参数

特点: 如果函数的形参个数不确定,那么可以使用动态参数获取实参;

具体情况:

  • arguments动态参数, arguments是一个数组,该数组中保存的就是实参;

剩余参数

特点:剩余参数也是一个数组,该数组中保存的实参;

语法function fn(...c) {};

⚠️注意事项:

  • 函数中只能设置一个剩余参数;
  • 函数中剩余参数可以和普通形参同时设置, 剩余参数必须写到形参的最后;
  • 函数中如果有形参和剩余参数,那么实参在赋值的时候,依然按照顺序先给形参赋值, 然后将多余的值赋值给剩余参数

箭头函数

语法: () => {};

代码实现

    let fn = (num1, num2) => {
    let sum = num1 + num2;
    return sum;
}
let res = fn(100, 2)
console.log(res);

解构赋值

1.数组解构赋值: 代码实现

let ary = [1, 2, 3]
let [num1, num2, num3] = ary;

2.对象解构赋值: 代码实现

    let obj = {
    uname: 'zs',
    age: 24,
    height: 170
}
let {uname: a, age: b, height: c} = obj;
console.log(a, b, c)    // zs 24。 170
// 简单写法:
let {属性名, 属性名} = 对象;

总结:

  • 对象解构赋值推荐简单写法,只写属性名;
  • 对象解构赋值中如果写了对象中不存在的属性, 得到的结果就是undefined

面向对象

语法:let 自定义对象名 = new Object();

语法解释

  1. 内置构造函数创建对象,对象名完全是自定义的;
  2. 内置构造函数中 new Object()是固定写法,注意自面大小写;
  3. new是一个关键字,new关键字就是用来调用构造函数创建对象的

备注:如果构造函数要创建对象,必须使用new关键字。

  1. Object()就是Js中内置的构造函数,构造函数本质上也是一个函数;

备注:构造函数和前面的函数区别在于构造函数就是用来创建对象的。

创建方法

1. 内置构造函数创造对象,将自己喜欢的人物,通过内置构造函数创造对象

代码实现

let Kobe = new Object();
Kobe.uname = '科比-布莱恩特';
Kobe.age = 41;
Kobe.work = () => {
    console.log('篮球运动员');
}
console.log(Kobe);

2. 工厂模式创建对象

1.创建出来的对象都是具有相同的属性或方法;

2.在程序中如果希望创建多个对象,那么推荐使用工厂方式;

3.工厂模式创建对象是开发人员自己总结而来。

代码实现

function student(stu_name, stu_age, stu_sex) {
    let stu = new Object();
    stu.name = stu_name;
    stu.age = stu_age;
    stu.sex = stu_sex;
    return stu;
}
​
let stu1 = student('张三', 23, '男')
let stu2 = student('李四', 22, '男')
let stu3 = student('小花', 18, '女')
console.log(stu1, stu2, stu3);

工厂方式创建对象总结

1.工厂方式创建对象本质上是将内置构造函数创建对象封装到一个函数中;

2.如果需要给对象加特定的属性,则单独加给对象即可。

3. 自定义构造函数创建对象

1.本质上就是工厂模式创建对象的简写;

代码实现

function Student(uname, age, sex) {
    this.stu_name = uname;
    this.stu_age = age;
    this.stu_sex = sex;
    this.work = () => {
        console.log('学生');
    }
}
​
//自定义构造函数创建对象
let student1 = new Student('张三', 23, '男')
console.log(student1);

构造函数总结

1.构造函数中的this指向谁?

  • 构造函数中的this指向是通过构造函数创建的对象(实例对象)

代码解释

// 由于this在局部作用域中,和student1不在同一个作用域,直接比较无意义,所以定义一个全局变量,将this赋值给全局变量obj再进行比较
let obj;
​
function Student(uname, age, sex) {
    this.stu_name = uname;
    this.stu_age = age;
    this.stu_sex = sex;
    this.work = () => {
        console.log('学生');
    }
    //将局部作用域的this赋值给全局变量obj
    obj = this;
}
​
//自定义构造函数创建对象
let student1 = new Student('张三', 23, '男')
//判断创建的全局变量和student1是不是相等
console.log(student1 === obj);   //true

2.工厂方式创建对象和自定义构造函数创建对象,到底使用哪个?

  • 原则上工厂方式和自定义构造函数都可以用,因为自定义构造函数就是工厂方式的简写;
  • 如果考虑代码是否简单,那么推荐使用自定义构造函数。

3.如何区别是构造函数还是普通函数?

  • 不管自定义构造函数还是普通函数 本质上都是函数;
  • 自定义构造函数作用是用来创建对象的,构造函数前面出现new关键字 ;
  • 如果是构造函数,构造函数的命名最好遵守帕斯卡命名法(函数名首字母大写)

对象中的成员(构造函数中的函数)

分类

1.实例成员

  • 在构造函数内部,通过this设置的属性和方法

2.静态成员

  • 在构造函数外部,通过构造函数设置的属性和方法

代码实现

//构造函数
function Student(uname) {
    //实例成员
    this.uname = uname;
    //实例成员
    this.eat = () => {
        console.log('吃饭');
    }
}
let zs = new Student('张三')
//静态成员
Student.age = 20;
//静态成员
Student.dance = () => {
  console.log('跳舞')
}
​
//调用实例成员
zs.uname;
zs.eat();
​
//调用静态成员
console.log(Student.age);   //20
Student.dance();   //跳舞

两者的特点(继承)

1.实例成员只能通过实例对象调用

  • 语法:实例对象.属性 实例对象.方法()

2.静态成员只能通过构造函数本身调用

  • 语法:构造函数.属性 构造函数.方法()

数组内置对象

数组内置对象(参考地址)

常用

1.数组反转----数组名.reverse();

2.数组合并-----数组名1.concat(数组名2)

3.数组中拼接字符串------数组名.join('符号'),返回的是字符串

4.获取数组中值的索引------数组.indexOf('值'),返回值是索引; ⚠️注意点:

  • indexOf()一次只能找到一个索引;
  • indexOf()从数组的开始位置向数组的结尾查找,如果找到直接返回,不会再向后面找;
  • 如果希望从指定位置找值所对应的索引,indexOf()设置第二参数即可;
  • 如果再数组中没有找到值所对应的索引,则返回-1;
  • lastindexOf()从右往左查找。

5.删除数组元素-------数组名.splice(index,删除元素个数);

  • index 表示从何处删除元素的下标值;

6.遍历数组---------forEach();

  • 语法:数组.forEach(function(item,index){})

代码实现

let ary = ['a', 'b', 'c'];
//  1.item表示数组的每一个值;
//  2.index表示数组中每一个值的索引;
ary.forEach(function (item, index) {
    console.log(index)     //0,1,2
})

7.筛选数组---------filter()

遍历数组并对数组的值进行筛选,将满足条件的结果找出来;

代码实现

let arr = [1, 2, 3, 4, 5, 6]
let res = arr.filter(function (item, index) {
    //  1.item表示数组的每一个值;
    //  2.index表示数组中每一个值的索引;
    return item % 2 === 0;
​
})
console.log(res)     //2,4,6

⚠️注意点:

  • filter本身具有遍历数组的功能,侧重点在于遍历数组的时候去找满足条件的值;
  • 如果需要filter实现筛选效果,加return条件即可;
  • forEach加条件不能实现筛选效果。

8.修改数组中所有的值------map()

  • 语法:数组.map(function(item,index){})

代码实现

let ary = [1, 2, 3, 4, 5]
let res = ary.map(function (item, index) {
    //  1.item表示数组的每一个值;
    //  2.index表示数组中每一个值的索引;
    return itme * 100;
})
console.log(res);    // 100,200,300,400,500

⚠️注意点:

  • 本身具有遍历数组的功能,但是侧重点在于修改数组中的值;
  • 如果希望修改数组中的值 return 修改的值。

将伪数组转化为真数组

语法: Array.from(伪数组名);

代码实现

let res = Array.from(btns);
res.push(123);
console.log(res);   //res为真数组,可以使用数组的属性和方法

字符串处理

方法

1.concat()方法-------连接合并字符串

  • 语法:字符串1.concat(字符串2);
  • 返回值:返回的结果是一个字符串

代码实现

let str1 = 'abc';
let str2 = 'bcd';
console.log(str1.concat(str2));    //  abcbcd

2.toUpperCase()toLowerCase() ---- 转化大小写

  • 大写:字符串.toUpperCase()
  • 小写: 字符串.toLowerCase()

代码实现

let str = 'abc123';
let str2 = 'ABCD1234';
str.toUpperCase()      //ABC123;
str2.toLowerCase()      //abcd1234    

3.startWith()endWith()---------判断字符串从那个值开始和结束

  • 字符串.startWith(值) 从那个值开始;
  • 字符串.endWith() 从那个值结束;
  • 返回值是true或者false。

代码实现

let str = 'ABC-123'
//开始
str.startsWith('ABC')    //true
//结束
str.endsWith('ABC')      //false

4.padStart(位数,'值')padEnd(位数,'值')

  • 作用:判断当前字符串是否满足对应的位数,如果不满足,则补充对应的值
  • padStart(位数,'值') ----在前面补充;
  • padEnd(位数,'值') ----在后面补充。

代码实现

let m = '1';
//前面
m.padStart(2, '0')//01
//后
m.padEnd(3, '0')      //100

5.split()----------分割字符串

  • 将字符串按照对应的字符进行分割(要以字符串的特点分割);
  • 语法字符串.split('分割符')
  • 返回值:分割完成后返回的是一个数组。

代码实现

let str = 'uname=laoduan';
let res = str.split('=')
console.log(res[1]);    //laoduan

6.substring(indexStart,indexEnd)---------截取字符串

  • 语法字符串.substring(indexStart,indexEnd);
  • 第一个参数从哪里开始,第一个值为0即索引开始;
  • 第二个参数从哪里结束,但不包含结束位置处的值;
  • 如果不设置第二个参数,默认截取到字符串末尾。

代码实现

let str = 'abcd';
str.substring(1, 2)    //b
str.substring(1)    //bcd

7.substr(start, length)--------截取字符串

  • 第一个参数从哪里开始,第一个值为0即索引开始;
  • 第二个参数表示要截取的长度。

代码实现

let str = 'abcd';
str.substr(1, 2)    //bc

8.字符串中的indexOf()lastIndexOf()方法

  • 语法字符串.indexOf('值')
  • 获取字符串中索引的值;
  • 完全和数组中的indexOf()lastIndexOf() 用法一样。

代码实现

let str = 'abcd';
str.indexOf('a')    //0

9.replace()--------替换字符串 语法

1.字符串.replace('要被替换的值', '替换后的值');

2.字符串.replace(正则, '替换后的值');

数据包装类型

程序在执行字符串的时候,将字符串进行了包装,将字符串包装成了一个数组对象 包装成了一个数组对象,所以字符串中才会有索引,才会有数组中的方法;

代码实现

    //字符串包装
let str1 = new String('abc');
    console.log(str1);     // string str[0] =a,str[1] =b,str[2] =c,

包装类型

  • 字符串包装类型:new String();
  • 数字包装类型:new Number();
  • 布尔包装类型:new Boolean();
  • 函数包装类型:new Function();

原型对象

1.什么是原型对象

定义:原型对象是构造函数的一个属性,这个属性叫prototype;

2.原型对象有什么用

1.原型对象prototype在构造函数中用来设置公共的方法;

2.设置公共的方法,避免内存的浪费。

3.如何使用原型对象

1.通过原型对象prototype设置一个公共的方法;

2.语法:构造函数.prototype.方法名 = function(){};

代码实现

function Student() {
​
}
​
Student.prototype.eat = () => {
    console.log('吃饭');
}
let zs = new Student();
let ls = new Student();
console.log(zs.eat === ls.eat);    //true --公共方法

对象原型

1.什么是对象原型

1.定义:对象身上的一个属性,这个属性叫做`__proto__;

  • 对象原型__proto__ 是两个横杠;
  • 在浏览器中[[prototype]]表示__proto__;

代码实现

function Student(uname) {
    this.uname = uname;
}
​
  Student.prototype.eat = () => {
    console.log('吃饭');
}
   let zs = new Student('张三');
   zs.eat();
   console.log(zs.__proto__);

对象原型的作用

1.对象原型__proto__默认指向了原型对象(prototype),所以对象才可以调用公共方法;

constructor

  • constructor是原型对象(prototype)和对象原型(proto)身上的一个属性;

代码实现

function Student(uname) {
            this.uname = uname;
        }
        Student.prototype.eat = function() {
            console.log('这是一个吃饭的方法')
        }
        console.log(Student.prototype);
        let zs = new Student('张三');
        console.log(zs.__proto__)

作用

1.保存一个构造函数;

2.提示当前对象属于那个构造函数;

3.记录父级的遗传信息。类似人类的DNA。

原型链

  • 在程序中指导方法查找执行规则的;

  • 先在对象本身上找方法,如果没有找到,沿着对象身上的proto去原型对象上找,直到找到为止。

call方法

1.通过call调用函数;

  • 语法:函数名.call();调用没有参数的函数 代码实现
function fn(){
 console.log(123);
}
fn.call();    //123
  • 语法:函数名.call(对象,实参,实参);调用有参数的函数

代码实现

function fn(a,b){
 console.log(a + b);
}
// 第一个参数对象,默认window对象
fn.call(window,2,4)   //6

2.调用函数的时候,修改this的指向;

  • 语法函数名.call(新对象);

  • 为什么在call方法中写一个新对象,函数中的this指向就发生了改变?

    • 本质上就是将新对象赋值给了函数中的this。

代码实现

function fn(){
 console.log(this);
}
  let obj = {
    uname :'zs'
  }
  fn.call(obj)
//this指向obj对象,本质上就是将obj对象赋值给了函数fn中的this

继承(组合继承)

定义:将Js中的方法继承和属性继承统称为组合继承;

优点:减少代码的重复,提高代码可读性。

实现代码(属性继承)

function Father(uname, age, height) {
        this.uname = uname;
        this.age = age;
        this.height = height
        //Father构造函数中的this指向的并不是zs,所以属性没办法绑定到zs上,需要修改this指向
    }
function Son(uname, age, height) {   
        //----删除Son中的属性,通过继承Father中的属性实现------
        //this.uname = uname;
        //this.age = age;
        //this.height = height;
    
       //------继承实现步骤-------
        //调用Father构造函数
        //将Father构造函数中的this指向Son构造函数的this
        Father.call(this, uname, age, height)   //重点理解
    }
​
    //通过Son构造函数创建一个实例对象
    let zs = new Son('张三', 22, 180)
    console.log(zs);

代码实现(方法继承)

function Father(uname, age, height) {
        this.uname = uname;
        this.age = age;
        this.height = height
        //Father构造函数中的this指向的并不是zs,所以属性没办法绑定到zs上,需要修改this指向
    }
    //设置一个公共的方法,减少内存使用
     Father.prototype.eat=()=>{
         console.log('这是一个吃饭的方法');
     }
function Son(uname, age, height) {
        //调用Father构造函数
        //将Father构造函数中的this指向Son构造函数的this
        Father.call(this, uname, age, height)
    }
    //实现方法继承
   Son.prototype =  Father.prototype     //-----重点
    //通过Son构造函数创建一个实例对象
    let zs = new Son('张三', 22, 180)
    //console.log(zs);
    zs.eat()     //这是一个吃饭的方法

类(Es6的写法)

  • 类的本质就是一个构造函数,作用就是用来创建对象。

类的使用步骤

  1. 定义类;
  2. 通过类创建对象;

语法

  • 定义类:class 自定义类名 {};

  • 通过类创建对象: let 对象名 = new 类名();

  • 代码演示

     class Student {}
         let zs = new Student();
         console.log(zs);
    

给类设置属性和方法

语法:

class Student {
         //设置属性
         constructor(stuname, stuage, stuheight) {
              this.stuname = stuname;
              this.stuAge = stuage;
              this.stuheight = stuheight;
         }
         // 方法
         eat() {
             console.log('吃饭的方法')
         }
         sing() {
             console.log('这是唱歌的方法');
         }
     }
​
     // 实例化一个对象
     let zs = new Student('张三', 23, 180);
​
     zs.eat();
     zs.sing();
     console.log(zs);

类的继承(Es6的写法)

语法

语法: 子类 extends 父类 {}

代码演示

class Father {
            //属性
            constructor(uname, uage, uheight) {
                this.uname = uname;
                this.uage = uage;
                this.uheight = uheight;
            }
            sing() {
                console.log('唱歌')
            }
​
            eat() {
                console.log('吃饭...')
            }
        }
        // 子类Son 继承父类
        class Son extends Father {}
        let zs = new Son('张三', 23, 180);
        zs.eat();
        zs.sing();
        console.log(zs);

super关键字

  • super()可以重新调用 父类的 constructor();
  • super()在调用 父类中的constructor()时候,还可以传递参数;
  • 子类在继承父类的时候,只要子类设置了自己的constructor() 必须在this前面加 super();

代码实现

class Father {
            //属性
            constructor(uname, uage, uheight) {
                this.uname = uname;
                this.uage = uage;
                this.uheight = uheight;
            }
            sing() {
                console.log('父亲中的唱歌')
            }
            eat() {
                console.log('父亲中的吃饭...')
            }
        }
        // 子类Son 继承父类
        class Son extends Father {
            // 子类中要设置自己特有的属性
            constructor(uname,uage, uheight, score) {
              
              //---------------- super关键字---------
              
                super(uname, uage, uheight);
              
                this.score = score;
            }
            eat() {
                console.log('子类中的吃饭...')
            }
        }
        let zs = new Son('张三', 23, 180, 100);
        zs.eat();

call、bind、apply区别

call

  • 作用:用来调用函数并修改this指向;

  • 语法:函数.call(对象,参数,参数)

  • 特点:call调用函数的时候,会立即执行函数中的代码;

  • 代码实现

function fn(){
  console.log(123);
}
fn.call();      //123 

bind

  • 作用:用来调用函数并修改this指向;

  • 语法:函数.bind(对象,参数,参数)

  • 特点:bind调用函数的时候,不会立即执行函数中的代码;

  • 如何要调用函数中的代码,必须要通过返回值实现

  • 实现代码

    function fn(){
      console.log(123);
    }
    let res = fn.bind();
    res();      //123

apply

  • 作用:用来调用函数并修改this指向;

  • 语法:函数.apply(对象,[参数,参数,参数])

  • 代码实现

    function fn(a,b){
        console.log(a+b);
    }
    fn.apply(window,[1,2])   //3
    

【结语】 希望这篇文章能帮助你对这些知识点有更深的理解,更文不易,欢迎点赞收藏。

【往期分享】