JS面试题—原型和原型链

一、 题目

  1. 如何准确判断一个变量是数组类型
  2. 写一个原型链继承的例子
  3. 描述new一个对象的过程

二、知识点

1.构造函数

    function Foo(name, age){
        this.name = name
        this.age = age
        this.class = 'class-1'
        // return this //默认有这一行
    }
    var f = new Foo('zhangsan', '20')
    //var f1 = new Foo('lisi', 22) //可创建多个对象

      特点:默认函数首字母大写,构造函数并没有显示返回任何东西。new 操作符会自动创建给定的类型并返回他们,当调用构造函数时,new会自动创建this对象,且类型就是构造函数类型。

2.构造函数—扩展

      var a = {} 其实是 var a = new Object()的语法糖
      var a = [] 其实是 var a = new Array()的语法糖
      function Foo(){…} 其实是 var Foo = new Function(){…}
      使用instanceof判断一个函数是否是构造函数

3.原型规则和示例
原型规则是原型链的基础

  1. 所有引用类型(数组、对象、函数),都具有对象特性,即可自由扩展属性(除‘null’以外)
  2. 所有引用类型(数组、对象、函数),都具一个_proto_ (隐式原型)属性,属性值是一个普通对象
  3. 所有函数,都有一个prototype(显式原型)属性,属性值也是一个普通对象
  4. 所有引用类型(数组、对象、函数),_proto_ (隐式原型)属性值指向它的构造函数的prototype(显式原型)属性值
  5. 当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么会去它的_proto_ (即它的构造函数的prototype)中寻找
    var obj = {}; obj.a = 100;
    var arr = []; arr.a = 100;
    function fn(){}
    fn.a = 100;  // 所有引用类型都能拓展属性

    console.log(obj.__proto__)
    console.log(arr.__proto__)
    console.log(fn.__proto__)  //所有引用类型都有_proto_(隐式原型)属性

    console.log(fn.prototype) //所有函数,都有一个prototype(显式原型)属性

    console.log(obj.__proto__ === Object.prototype)// true 引用类型_proto_(隐式原型)属性值指向它的构造函数的prototype(显式原型)属性值
    //当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么会去它的*_proto_* (即它的构造函数的prototype)中寻找
    //构造函数
    function Foo(name, age){
        this.name = name;
    }
    Foo.prototype.alertName = function(){
        alert(this.name)
    }
    //创建实例
    var f = new Foo('zhangsan');
    f.printName = function(){
        console.log(this.name);
    }
    //测试
    f.printName(); //f自身有printName这个属性,所以直接可以调用
    f.alertName(); //f自身没有alertName这个属性,所以会去它的_proto_(即它的构造函数的prototype)中寻找

      tips:循环对象自身的属性时,建议加上hasOwnProperty来保证程序的健壮性

    var item;
    for(item in f){
        //高级浏览器已经在for in 中屏蔽了来自原型的属性
        //但是这里建议大家还是加上这个判断,保证程序的健壮性
        if(f.hasOwnProperty(item)){
            console.log(item)
        }
    }

4.原型链
还是采用上面构造函数的例子来解释原型链

    //构造函数
    function Foo(name, age){
        this.name = name;
    }
    Foo.prototype.alertName = function(){
        alert(this.name)
    }
    //创建实例
    var f = new Foo('zhangsan');
    f.printName = function(){
        console.log(this.name);
    }
    //测试

    f.toString(); //要去f._proto_._proto_中查找
    /*
     f自身没有toString这个属性, 那么f会去它的隐式原型_proto_(即它的构造函数的prototype)中寻找,而f的构    造函数也没有toString这个属性,那该往哪里找?
     原型规则中第二、三条提到:所有引用类型(数组、对象、函数),都具一个_proto_(隐式原型)属性,属性值是一个普通对象,意思f._proto_(f.prototype)是个对象
     f._proto_是个对象,那么该往f._proto_隐式原型中寻找,即f._proto_._proto_  (f._proto_寻找其对象的构造函数也就是Object)
    * */

JS面试题—原型和原型链_第1张图片

5.instanceof

用于判断引用类型属于哪个构造函数的方法

f instanceof Foo的判断逻辑:
      f的proto一层一层往上,能否找到Foo.prototype
      再试着判断f instanceof Object

三、解答

1.如何准确判断一个变量是数组类型

    var arr = []
    arr instanceof Array //true
    typeof arr // Object typeof是无法判断是否是数组的

2.写一个原型链继承的例子

//写一个封装DOM的例子
    function Elem(id){
        this.elem = document.getElementById(id);
    }
    Elem.prototype.html = function (val) {
        var elem = this.elem;
        if(val){
            elem.innerHTML = val;
            return this;  //链式操作, 可有可无
        }else{
            return elem.innerHTML;
        }
    }
    Elem.prototype.on = function(type, fn){
        var elem = this.elem;
        elem.addEventListener(type, fn);
        return this;
    }
    var div1 = new Elem('div1');
    //console.log(div1.html())
    div1.html('

hello world

').on('click', function(){ alert('clicked'); }).html('

javascript

')

3.描述new一个对象的过程

  1. 创建空对象;
      var obj = {};
  2. 设置新对象的constructor属性为构造函数的名称,设置新对象的_proto_属性指向构造函数的prototype对象;
      obj._proto_ = ClassA.prototype;

  3. 使用新对象调用函数,函数中的this被指向新实例对象:
      ClassA.call(obj);  //{}.构造函数();

  4. 将初始化完毕的新对象地址,保存到等号左边的变量中

tips:若构造函数中返回this或返回值是基本类型(number、string、boolean、null、undefined)的值,则返回新实例对象;若返回值是引用类型的值,则实际返回值为这个引用类型。

你可能感兴趣的:(javascript,html)