js知识回顾

68 阅读4分钟

JS基础知识 - 1

js的数据类型

  1. string
  2. number
  3. boolean
  4. null
  5. undefined
  6. bigint
  7. symbol
  8. object

js的原型链

原型链涉及的概念非常多,首先就是原型是什么?

const a = {}

当我创建一个空对象a,我调用a.toString()竟然有返回值,这是因为a.__proto__ === Object.prototype,Object.prototype上定义了toString方法,所以a可以使用。我们叫Object.prototype是a的原型

const a = []

在这个例子中a的原型是Array.prototype而Array.prototype的原型是Object.prototype,此时a.toString()会向上查找原型上是否存在toString,一直找到Object.prototype上,这样一个对象指向其原型对象的内部链接,我们称这为原型链

const x = Object.create(原型)

设置一个对象的原型可以使用Object.create方法。该对象将沿着原型链继承原型对象上的所有属性和方法。

在没有 Class 的情况下实现「继承」。以 a ===> Array.prototype ===> Object.prototype 为例,我们说:

  1. a 是 Array 的实例,a 拥有 Array.prototype 里的属性

  2. Array 继承了 Object

  3. a 是 Object 的间接实例,a 拥有 Object.prototype 里的属性

这样一来,a 就既拥有 Array.prototype 里的属性,又拥有 Object.prototype 里的属性。

这种方式与class相比,它不支持私有属性

this到底是什么?

在JavaScript中,this关键字表示当前函数的执行上下文。它通常用于引用当前函数所在的对象或者作用域。实际上,this的值取决于当前代码所在的执行上下文。如果当前代码在一个函数中执行,则this通常引用该函数所在的对象;如果当前代码在全局上下文中执行,则this引用全局对象(在浏览器中是 window 对象)

函数通常有以下几种调用方式:

fn()
obj.fn()
fn.call()
fn.apply()

我们可以将前两种调用方式改写成.call()形式

fn.call(undefined)
fn.call(obj)

这样this的值就很好确定了

fn.call(undefined) // this = window
fn.call(obj) // this = obj

构造函数的 new

在 JavaScript 中,当你使用 new 关键字创建一个对象时,实际上发生了以下几个步骤:

  1. 创建临时对象/新对象
  1. 绑定原型
  1. 指定 this = 临时对象
  1. 执行构造函数
  1. 返回临时对象

立即执行函数

(function () {
console.log(1)
})()

声明一个匿名函数并立即执行这个函数,这种做法就叫立即执行函数

立即执行函数还有以下几种声明方式:

(function(){alert('我是匿名函数')} ())  // 用括号把整个表达式包起来
(function(){alert('我是匿名函数')}) ()  // 用括号把函数包起来
!function(){alert('我是匿名函数')}()    // 求反,我们不在意值是多少,只想通过语法检查。
+function(){alert('我是匿名函数')}()
-function(){alert('我是匿名函数')}()
~function(){alert('我是匿名函数')}()
void function(){alert('我是匿名函数')}()
new function(){alert('我是匿名函数')}()
var x = function(){return '我是匿名函数'}()

立即执行函数的作用就是创建一个独立的作用域,避免全局作用域中的变量污染和命名冲突

在ES6中我们可以用以下方式创建局部作用域:

{
    let a = 1
    console.log(a)
}

js中的闭包

闭包是js的一种语法特性,简而言之就是一个函数内部创建一个函数,此函数可以访问外部函数的局部变量和参数。 下面是一个闭包示例:

function out() {
    let count = 0;
    function add() {
        count++;
        console.log(count)
    }
    return add;
}

const fn = out()
fn() // 1

解决了什么问题:

  1. 避免污染全局环境。(因为用的是局部变量)

  2. 提供对局部变量的间接访问。(因为只能 count += 1 不能 count -= 1)

  3. 维持变量,使其不被垃圾回收。

闭包使用不当可能造成内存泄露。

js实现类

方法一: 使用原型

// 定义一个类
function Person(name, age) {
    this.name = name;
    this.age = age;
}

// 定义一个方法
Person.prototype.say() = function() {
    console.log(`hi! my name is ${this.name}`)
}

// 创建实例
let person = new Person('tom', 18)
person.say()

方法二: 使用 class

class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
    
    say() {
        console.log(`hi! my name is ${this.name}`)
    }
}

const person = new Person('jack', 19)
person.say()

需要注意的是,在 JavaScript 中类的实现并不是真正的类,而是使用函数和原型链的方式来模拟类的概念。

js实现继承

方法一: 使用原型链

子类的原型对象中设置父类的实例对象,从而继承父类的属性和方法

// 定义一个父类
function Animal(name) {
    this.name = name;
}

Animal.prototype.run = function() {
    console.log('run')
}

// 定义一个子类

function Dog(name, color) {
    Animal.call(this, name); // 调用父类的构造函数
    this.color = color;
}
Dog.prototype = new Animal(); // 将子类的原型对象设置为父类的实例对象
Dog.prototype.constructor = Dog // 修正子类的 construuctor

// 定义子类的方法
Dog.prototype.bark = function () {
    console.log('叫')
}

// 创建实例
const dog1 = new Dog('tom', 'red')
dog1.run()
dog1.bark()

如果只想要父类的方法不想要属性

var f = function(){ }
f.prototype = Animal.prototype
Dog.prototype = new f()

方法二: 使用class

class Animal{
 constructor(legsNumber){
   this.legsNumber = legsNumber
 }
 run(){}
}
class Dog extends Animal{
 constructor(name) {
   super(4)
   this.name = name
 }
 say(){
   console.log(`汪汪汪~ 我是${this.name},我有${this.legsNumber}条腿。`)
 }
}