js复习总结(一)

104 阅读7分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 3 天,点击查看活动详情

1.JS由哪三部分组成?

  • ECIAScript: Js的核心内容,描述了语言的基础语法
  • 文档对象模型(DOM) :DOM把整个HTML页面规划为元素构成的文档
  • 浏览器对象模型(BOM):对浏览器窗口进行访问和操作

2.JS有哪些内置对象?

string Boolean Number Array object Function Math Date RegExp...

Math

abs() sqrt( ) max( ) min( )

Data

new Data() getYear()Array

string

concat() length ·slice() · split()

3.操作数组的方法有哪些?

push( ) pop( ) sort( ) splice( ) unshift( ) shift( ) reverse( ) concat( ) join( ) map() filter() ervery() some( ) reduce( ) isArray( ) findIndex()

哪些方法会改变原数组? push() pop() unshift( ) shift( ) sort( ) reverse( ) splice()

4.JS对数据类的检测方式有哪些?

  • typeof()对于基本数据类型没问题,遇到引用数据类型就不管用
  • instanceof() 只能判断引用数据类型,不能判断基本数据类型
  • constructor几乎可以判断基本数据类型和引用数据类型。如果声明了一个构造函数,并把它的原型指向了Array会失效
  • Object.prototype.tostring.call()
console.log( typeof (666) ) // number
console.log( [] instanceof Array ) //true
console.log( ('abc').constructor === string ) //true
var opt = Object.prototype.tostring
console.log( opt.call('123') ) // number

5.说一下闭包,闭包有什么特点?

函数嵌套函数,内部函数被外部函数返回并保存下来时,就会产生闭包

特点:可以重复利用变量,并且这个变量不会污染全局的一种机制;这个变量是一直保存再内存中 不会被垃圾回收机制回收。

缺点:闭包较多的时候,会消耗内存,导致页面的性能下降,在IE浏览器中才会导致内存泄漏

使用场景:防抖,节流,函数嵌套函数避免全局污染的时候

6.前端的内存泄漏怎么理解?

JS里已经分配内存地址的对象,但是由于长时间没有释放或者没办法清除,造成长期占用内存的现象,会让内存资源大幅浪费,最终导致运行速度慢,甚至崩溃的情况。

内存泄漏的因素:一些未声明直接赋值的变量;一些未清空的定时器;过度的闭包;一些引用元素没有被清除

7.事件委托是什么?

又叫事件代理,原理就是利用了事件冒泡的机制来实现,也就是说把子元素的事件绑定到了父元素的身上如果子元素组织了事件冒泡,那么委托也就不成立。

组织事件冒泡: event.stopPropagation()

好处:提高性能,减少事件的绑定,也就减少了内存的占用。

ul.onclick = function (event) {
    event = event || window.event;
    var target = event.target;
    // 获取目标元素
    if (target.nodeName == 'LI') {
        alert(target.innerHTML);
    }
}
    
jquery
    
$("#app").delegate(":button","click",function(){
    alert($(this).attr("id"));
});

8.基本数据类型和引用数据类型的区别?

基本数据类型: string Number Boolean undefined null---基本数据类型保存在栈内存当中,保存的就是一个具体的值

引用数据类型(复杂数据类型): object Function Array---保存在堆内存当中,声明一个引用类型的变量,它保存的是引用类型数据的地址,假如声明两个引用类型同时指向了一个地址的时候,修改其中一个那么另外一个也会改变

9.说一下原型链。

原型就是一个普通对象,它是为构造函数的实例共享属性和方法;所有实例中引用的原型都是同一个对象

使用prototype可以把方法挂在原型上,内存值保存一份

__proto__可以理解为指针,实例对象中的属性,指向了构造函数的原型(prototype)

一个实例对象在调用属性和方法的时候,会依次从实例本身、构造函数原型、原型的原型上去查找

image.png

10.new过程发生了什么

  1. 先创建一个空对象
  2. 把空对象和构造函数通过原型链进行链接·//·
  3. 把构造函数的this绑定到新的空对象身上
  4. 根据构建函数返回的类型判断,如果是值类型,则返回对象,如果是引用类型,就要返回这个引用类型

11.js如何实现继承

  1. 原型链继承
  2. 借用构造函数继承
  3. 组合继承
  4. class继承
    让一个构造函数的原型是另一个类型的实例,那么这个构造函数new出来的实例就具有该实例的属性。
    function Parent() {
        this.isShow = true
        this.info = {
            name: "abc",
            age: 18,
        };
    }
    Parent.prototype.getInfo = function () {
        console.log(this.info);
        console.log(this.isShow);
    }
    function Child() { };
    Child.prototype = new Parent(); // 就是这句话
    let Child1 = new Child();
    Child1.info.gender = "男";
    Child1.getInfo(); // {name: 'abc', age: 18, gender: '男'} ture

    let child2 = new Child();
    child2.isShow = false
    console.log(child2.info.gender) // 男
    child2.getInfo(); // {name: 'abc', age: 18, gender: '男'} false
    优点:写法方便简洁,容易理解。
    缺点:对象实例共享所有继承的属性和方法。无法向父类构造函数传参。


    
    
在子类型构造函数的内部调用父类型构造函数;使用 apply() 或 call() 方法将父对象的构造函数绑定在子对象上。
    function Parent(gender) {
        this.info = {
            name: "yyy",
            age: 18,
            gender: gender
        }
    }

    function Child(gender) {
       Parent.call(this, gender)
    }

    let child1 = new Child('男');
    child1.info.nickname = 'xxxx'
    console.log(child1.info);

    let child2 = new Child('女');
    console.log(child2.info);
    优点:解决了原型链实现继承的不能传参的问题和父类的原型共享的问题。
    缺点:借用构造函数的缺点是方法都在构造函数中定义,因此无法实现函数复用。
        在父类型的原型中定义的方法,对子类型而言也是不可见的,结果所有类型都只能使用构造函数模式。

    将 原型链 和 借用构造函数 的组合到一块。使用原型链实现对原型属性和方法的继承,
     而通过借用构造函数来实现对实例属性的继承。这样,既通过在原型上定义方法实现了函数复用, 
     又能够保证每个实例都有自己的属性 
     function Person(gender) {
         console.log('执行次数');
          this.info = {
             name: "yyy",
             age: 19,
             gender: gender
         }
     }

     Person.prototype.getInfo = function () {   // 使用原型链继承原型上的属性和方法
         console.log(this.info.name, this.info.age)
     }

    function Child(gender) {
        Person.call(this, gender) // 使用构造函数法传递参数
     }

    Child.prototype = new Person()

     let child1 = new Child('男');
     child1.info.nickname = 'xxx'
     child1.getInfo()
     console.log(child1.info);

     let child2 = new Child('女');
     console.log(child2.info);
     优点:就是解决了原型链继承和借用构造函数继承造成的影响。
     缺点:是无论在什么情况下,都会调用两次父类构造函数:
         一次是在创建子类原型的时候,另一次是在子类构造函数内部

    class Animal {
        constructor(kind) {
            this.kind = kind
        }
        getKind() {
            return this.kind
        }
    }

    // 继承Animal
    class Cat extends Animal {
        constructor(name) {
            // 子类的构造方法中必须先调用super方法
            super('cat');
            this.name = name;
        }
        getCatInfo() {
            console.log(this.name + ':' + super.getKind())
        }
    }

    const cat1 = new Cat('buding');
    cat1.getCatInfo(); // buding:cat
    优点:语法简单易懂,操作更方便。
    缺点:并不是所有的浏览器都支持class关键字

12.JS的设计原理是什么?

  • JS引擎
  • 运行上下文
  • 调用栈
  • 事件循环
  • 回调

13.JS中关于this指向的问题

  1. 全局对象中的this指向指向的是window
  2. 全局作用域或者普通函数中的this指向全局window
  3. this永远指向最后调用它的那个对象,在不是箭头函数的情况下
  4. new关键词改变了this的指向
  5. apply,call,bind 可以改变this指向,不是箭头函数
  6. 箭头函数中的this,它的指向在定义的时候就已经确定了,箭头函数它没有this,看外层是否有函数,有就是外层函数的this,没有就是window
  7. 匿名函数中的this永远指向了window,匿名函数的执行环境具有全局性,因此this指向window

14.script标签里的async和defer有什么区别?

  • 当没有async和defer这两个属性的时候,浏览器会立刻加载并执行指定的脚本
  • 有async,加载和渲染后面元素的过程将和script的加载和执行并行进行(异步)
  • 有defer,加载和渲染后面元素的过程将和script的加载并行进行(异步),但是它的执行事件要等所有元素解析完成之后才会执行

有了defer可以保证加载好的js脚本按顺序执行,不会出现async那样谁先加载好谁先执行,更没有阻塞的过程。

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 3 天,点击查看活动详情