JS知识点补充

109 阅读5分钟

JS知识点补充

1如果你要改一个数组的其中一个值是能改的,但是字符串是不能单独修改其中的值的

//数组
let arr = [1, 2, 3];
arr[1] = 1;
console.log(arr); //[ 1, 1, 3 ]
//字符串不能修改
let arr = 'hello';
arr[1] = 1;
console.log(arr); //hello

2在函数形参中对对象解构赋值同时也需要添加默认值的时候,注意以下

//对象的结构赋值
let obj = {name: 'nodejs', age: 11};
function func({name, age}) {
    console.log(name, age);
}
func(obj); //nodejs 11//没有传入实参会报错
function func({name, age}) {
    console.log(name, age);
}
func(); //报错//没有传入实参,但是相当于添加了默认值undefined
function func({name, age} = {}) {
    console.log(name, age);
}
func(); //undefined undefined//**********
//没有传入实参,但是分别添加了默认值,注意{name = "nodejs", age = 11}是添加默认值而不是一个对象
//形参中必须这样写{name = "nodejs", age = 11} = {},相当于之前先添加默认值undefined,然后又分别把默认值改为了'nodejs'和11
//只这样写{name = "nodejs", age = 11}会报错,因为这样写既不是对象的格式,也不是对象添加默认值的样式
function func({name = "nodejs", age = 11} = {}) {
    console.log(name, age);
}
func(); //nodejs 11

3箭头函数中,函数体只有一个语句可以省略大括号,这种化简的写法是会返回一个值,给到函数的调用结果

let func = x1 => console.log(x1);
let ret = func(10); //获取这个函数的返回值,因为console.log的执行结果就是undefined,
//运行完之后相当于return undefined
console.log(let); //undefined
​
let func2 = () => 999 + 1;
let ret2 = func2();
console.log(ret2); //1000,这个有返回值
//let func2 = () => 999 + 1;这条语句相当于let func2 = () => {return 999 + 1};

如果函数体里只有一个语句且其返回值是一个对象,不能用简写的方式,因为大括号是函数体的还是对象的意义不明,会报错

4this的指向问题

function People(name, age) {
    this.name = name;
    this.age = age;
    this.say = function() {
        console.log(this.name); //this指向调用者
    }
}
let p1 = new People("zhangsan", 18);
p1.say(); //zhangsan
​
function People(name, age) {
    this.name = name;
    this.age = age;
    this.say = function() {
        setTimeout(function() {
            console.log(this.name); //定时器里的function是一个新开辟的局部作用域,有自己的作用域,所以this指向丢失
        }, 1000)
        
    }
}
let p2 = new People("zhangsan", 18);
p2.say(); //undefined
​
//之前用这种方式
function People(name, age) {
    this.name = name;
    this.age = age;
    this.say = function() {
        let _this = this;
        setTimeout(function() {
            console.log(_this.name); 
        }, 1000)
        
    }
}
let p2 = new People("zhangsan", 18);
p2.say(); //zhangsan
​
//也可以使用箭头函数,箭头函数没有自己的作用域,它的作用域和外层是相同的
function People(name, age) {
    this.name = name;
    this.age = age;
    this.say = function() {
        setTimeout(() => {
            console.log(this.name); 
        }, 1000)
        
    }
}
let p2 = new People("zhangsan", 18);
p2.say(); //zhangsan
​
//在DOM操作中也可能遇到这种情况
let oDom = document.getElementById("odiv");
oDom.onclick = function() {
    setTimeout(function() {
        this.style.width = "500px"; //会出现问题
    }, 1000)
}
​
//可以使用箭头函数进行修改
let oDom = document.getElementById("odiv");
oDom.onclick = function() {
    setTimeout(() => {
        this.style.width = "500px";
    }, 1000)
}
​
//如果是以字面量形式创建的对象,不适合书写箭头函数
let obj = {
    name: "nodejs",
    age: 11,
    say: () => {
        console.log(this.name);
    }
}
obj.say(); //undefined,因为箭头函数this指向外层,这里指向的是Windows,而不是指向obj
//所以要用之前常用的写法
let obj = {
    name: "nodejs",
    age: 11,
    say: function() {
        console.log(this.name);
    }
}
obj.say(); //nodejs

5ES6中创建类中constructor是什么时候执行的?

创建对象的时候执行的/实例化的时候

class Animal {
    constructor(name) {
        this.name = name;
    }
    showName() {
        ...
    }
}
let dog = new Animal("小白");

6ES5中的静态方法,ES6的形式也需要掌握

//定义类
function Animal(name) {
    this.name = name;
}
//给类的实例定义方法,通过实例调用
Animal.prototype.showName = function () {
    console.log(this.name);
}
//给类定义静态方法,必须通过类来调用
Animal.eat = function () {
    console.log("进食");
}
//根据函数实例化对象
var a = new Animal("Tom");
//调用对象的方法
a.showName();
//调用类的静态方法
Animal.eat();

实例属性和实例方法都是给实例对象去调用的;

每一个实例对象在内存中是独立的,各自用自己的属性和方法;

let dog = new Animal("小白");左右哪些事情?1创建了新的对象2调用了constructor方法并传入对应的参数3把这个对象的指向给到dog标识符这个内存空间;

因为上条第3点中传入参数并执行了constructor方法,所以新对象才有了这些属性。

静态方法是通过类名来调用的

在类中,某个方法如果不用到this,我们可以理解为这个方法就不是给实例用的,可以设置为静态方法

//静态属性
//在ES6中静态方法是在class中用static来定义的,但是不能用static来定义静态属性,typeScript可以这样定义,而ES6中要是想定义静态属性,可以在class Animal类外面用Animal.num = 10;这样来定义
//ES6中静态方法也可以通过类的extends继承拿到

7在类的继承中如果子类有和父类相同的方法名,子类实例调用该方法时调用的是子类的方法(这就叫做重写),同理当子类中没有constructor时,子类可以调用父类的属性,但是子类中有constructor的时候,相当于重写了,此刻子类实例只能调用子类的constructor里的属性,如果想要调用父类的属性,需要用super()调用回父类的constructor,父类中constructor()需要的形参在super()中传入,在创建子类对象的时候需要传入实参,所以子类的constructor中需要相应的形参,通过子类的constructor形参传递到super中的参数,再调用回父类的constructor中;子类中的this是在调用super方法之后才起作用,所以super要放在constructor内的第一行位置

8一个js文件就是一个模块,提高代码的可维护性,其次可以复用,编写代码不必从零开始,还可以避免函数名和变量名冲突,模块化主要是方便项目的开发和维护

模块的作用域是私有的,内部定义的变量或者函数,只有当前的文件/模块可以使用,其他模块需要使用的话,以CommonJS的Modules规范:Nodejs为例,需要导出exports或者module.exports,导出的时候,以对象的方式进行导出,在要用的模块内,需要先引入导出的那个模块,使用require引入,引入后需要用一个变量来接收导入的对象。

模块化定义规范:

AMD规范: Require.js

CMD规范: Sea.js

//AMD和CMD是Nodejs出来之前使用的老的规范

CommonJS的Modules规范: Nodejs

ES6模块化规范