js-常见题目

79 阅读7分钟

new干了什么事

  1. 创建一个新对象
  2. 让新创建的子对象继承构造函数的原型对象(自动设置子对象的__proto__属性,指向构造函数的原型对象)
  3. 调用构造函数,将this指向这个新对象。在构造函数内为这个新对象添加对应的属性和方法
  4. 返回这个新对象的地址

this的指向情况

  1. obj.fun() fun中的this指向obj
  2. new Fun() Fun中的this指向创建的新对象
  3. 原型对象当中的this指向他的调用者
  4. fun()、匿名函数自调、回调函数中的this指向windows。严格模式下this为undefined
  5. DOM事件函数里的this指向当前正在触发事件的DOM元素对象
  6. Vue当中this默认都指向当前的vue对象
  7. 箭头函数的this指向当前函数之外最近作用域中的this(箭头函数底层原理为bind,bind是永久绑定,不可替代)
  8. call、apply、bind修改this

call、apply、bind

要调用的函数.call(替换this的对象,实参1,...)

call做的3件事

  • 立刻调用一次点前的函数
  • 自动将.前的函数中的this替换为指定的新对象
  • 还要向要调用的函数中传实参值
要调用的函数.apply(替换this的对象,包含实参值的数组)

apply做的3件事

  • 调用.前的函数
  • 替换.前的函数中的this为指定的对象
  • 拆散数组为多个元素值,分别传给函数的形参变量
var 新函数 = 原函数.bind(替换this的对象,不变的实参值)

bind做的2件事

  • 创建一个和原函数一样的新函数副本
  • 永久替换新函数中的this
  • 永久替换部分形参变量为固定的实参值

用bind替换的this就不能再次替换this了

创建对象的方法

new Object()

缺点:步骤多

字面量

var 对象名 = {}

缺点:如果反复创建多个对象,代码很冗余

工厂函数方式

function obj(name,age){
    var o = new Object()
    o.name = name
    o.age = age 
    o.say = function(){
        console.log('工厂函数')
    }
    return o
}
var p = obj('nh',25)

缺点:本质还是Object(),将来无法跟据对象的原型对象准确判断对象的类型

构造函数方式

  • 先用构造函数定义一类对象统一属性结构和方法
  • 再用new调用构造函数,反复创建相同属性结构,不同属性值的多个对象
function Fn(name,age){
    this.name = name
    this.age = age
    this.fn1=function(){...}
}
var a = new Fn('nh',25)

缺点:如果构造函数中包含方法,则重复创建,浪费内存

原型对象方式

先创建完全相同的对象,再给子对象添加个性化属性

function Fn(){}
Fn.prototype.name = 'nh'
Fn.prototype.age = 25
Fn.prototype.fn = function(){
    ....
}
var f = new Fn()
f.name = 'bh'//不会修改Fn原型上的name,只会在当前对象上添加一个name属性

混合模式

先创建完全相同的对象,再给子对象添加个性化属性

function Fn(name,age){
    this.name = name
    this.age = age
}
Fn.prototype.fn=function(){
    console.log('Fn函数')
}
var a = new Fn('nh',25)

缺点:不符合面向对象封装的思想

动态混合模式

function Fn(name,age){
    this.name = name
    this.age = age
    if(Fn.prototype.fn==="undefined"){
        Fn.prototype.fn(){
            console.log(this.name)
        }
    }
}
var a = new Fn('nh',25)

寄生构造

构造函数里调用其他构造函数

function Fn(name,age){
    this.name = name
    this.age = age
    if(Fn.prototype.fn==="undefined"){
        Fn.prototype.fn(){
            console.log(this.name)
        }
    }
}
function Fn1(name,age){
    var a = new Fn(name,age)
    a.sex = 1
    return a
}
var b =new Fn1('nh',25)

ES6 Class

class Fn{
    constructor(name,age){
        this.name = name
        this.age = age
    }
    fn(){
        console.log(this.name,this.age)
    }
}
var a = new Fn('nh',25)
a.fn()

闭包,不用this,不用new!安全,可靠

function Fn(name,age){
    var a = {}
    a.getName = function(){
        return name
    }
    a.setName = function(value){
        name = value
    }
    return a
}
var b = Fn('nh',25) 
console.log(b.getName())

继承

原型对象实现继承

构造函数中都有一个自带的属性prototype,指向自己配对的原型对象

let obj = {}
obj.prototype.属性名=属性值
obj.prototype.方法名=function(){...}

function Father() {}
var father = new Father();
Father.prototype.a = 1;
Father.prototype.fn = function () {
  console.log('Father中函数');
}

//继承父类的实例
function Son() {}
// 子类原型继承父类的实例
Son.prototype = father;
var son = new Son()
console.log(son.a)
a.fn()
//通过 Son.prototype = father实现继承,改变子级的属性父级不会改变。但如果父级的圆形链上属性发生改变,子级的也会改变。

//继承父类的原型
function Son1() {}
Son1.prototype = Father.prototype;
var son = new Son1();
//原型的继承他们使用 同一个原型,会造成原型的污染

call方法继承(不会继承原型)

function Father(name,age,sex) {
    this.name = name;
    this.age = age;
    this.sex = sex;
}
Father.prototype.a = 1;
Father.prototype.fn = function () {
  console.log('Father中函数');
}

function Son(name,age) {
    Father.call(this,name,age)
}
var son = new Son('nh',25);

ES6当中的extends

class Father{
    constructor(name,age){
        this.name = name
        this.age = age
    }
    fn(){
        console.log('父级方法')
    }
}
class Son extends Father{
    constructor(name,age){
        super(name,age)//调用父类构造方法
    }
}
let son = new Son('nh',25)
console.log(son.name,son.age)
son.fn()

实例继承

function Father(name,age) {
    this.name = name;
    this.age = age;
}
Father.prototype.a = 1;
Father.prototype.fn = function () {
  console.log('Father中函数');
}

function Son(name,age) {
    var a = new Father(name,age)
    a.sex = 1
    return a
}
let b = new Son('nh',25)

组合继承

function Father(name,age) {
    this.name = name;
}
Father.prototype.a = 1;
Father.prototype.fn = function () {
  console.log('Father中函数');
}


function Son(name,age) {
    Father.call(this)
    this.age = age
}
Son.prototype = new Father()
Son.prototype.constructor = Son 
var son = new Son()

原型链

  • 由多级父对象逐级继承形成的链式结构。
  • 保存着一个对象可用的所有属性和方法
  • 控制着属性和方法的使用顺序(就近原则,先子集后父级)

ES6

模板字符串

${}里面可以放有返回值的合法的js表达式。不能放没有返回值的js表达式,也不能放分支/判断,循环等程序结构

let const

var问题:

  • 声明提前
  • 没有块级作用域 let、const:
  • 不会声明提前
  • 有块级作用域(底层会被翻译为匿名函数自调)
  • 相同作用域内,静止声明两个同名的变量
  • 全局声明变量,window当中找不到这个变量
  • const声明为常量,不能修改实际原始类型的值和引用类型的地址,必须在声明的时候赋值

箭头函数

  • this指向当前函数之外最近作用域中的this(箭头函数底层原理为bind,bind是永久绑定,不可替代)
  • 构造函数不能使用
  • 对象的方法不能使用
  • 原型对象方法不能使用
  • DOM中事件处理函数不能用
  • 箭头函数无法使用call,apply,bind改变this
  • 箭头函数不支持arguments
  • 箭头函数没有prototype

for of

forEach只能遍历数字下标的索引数组,无法遍历类数组对象

for of

  • 可以遍历数字下标的的索引数组以及类数组对象
  • 无法获得下标位置i,只能获得元素值
  • 无法控制遍历的顺序或步调,只能从头到尾,一个一个的顺序遍历
  • 无法遍历下标名为自定义下标的对象和关联数组
  • 只能遍历实现iterable的对象
  • 遍历对象用for in,普通对象未实现iterable,for in遍历普通对象的每一项为对象的value,遍历数组为数组的下标

参数增强

  • 参数默认值
  • 剩余参数,...数组名

展开运算符

"..."

  • 复制数组(浅拷贝)
  • 合并数组
  • 克隆对象(浅克隆)
  • 合并多个对象和属性

解构

  • 数组解构
  • 对象解构
  • 参数解构

Class&继承

  • 直接放在class{}内的方法定义,其实还是保存在原型对象中的
  • 在class中定义的属性,不会保存在原型对象中,而是成为每个子对象的自有属性
  • 静态属性:不需要创建子对象,单靠类名就可以直接访问的属性。静态属性存在构造函数对象上的

extends:只是设置子类的原型对象继承父类型的原型对象、只能保证孙子对象可以使用爷爷类型原型对象中的共有方法、暂时无法为孙子对象补全缺少的自有属性。可以使用super关键字,super指向父类型

JS中的内置类型

  • String、Number、Boolean
  • Array、Date、RegExp
  • Math(不是类型,是一个对象)
  • Error
  • Function Object
  • global(全局作用域对象、在浏览器中被windows代替)