【JS面试】第三章 原型与原型链

  1. 如何准确判断一个变量是数组类型
  2. 写一个原型链继承的例子
  3. 描述new一个对象的过程
  4. zepto(或其他框架)源码中如何使用原型链

1 构造函数

  • 构造函数首字母大写
  • 构造函数类似于模板

new一个构造函数,返回一个对象的过程

  1. new的时候把参数传入也可不传
  2. new函数执行时,创建一个空对象
  3. this指向这个新对象this = {}
  4. 执行代码,即对this.name等开始顺序赋值
  5. 赋值完后,默认return this
  6. 赋值给ff.namef.agef.class生效
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', 23); 可创建多个对象

2 构造函数-扩展

  • var a={}其实是var a=new Object()的语法糖
  • var a=[]其实是var a=new Array()的语法糖
  • funtion() Foo(){...}其实是var Foo=Function(){...}的语法糖
  • 所有的引用类型(对象、数组、函数)都有构造函数
  • 推荐使用前者的写法
  • 使用instanceof判断一个函数是否是一个变量的构造函数

3 5条原型规则和示例

  1. 所有的引用类型(数组、对象、函数),都具有对象特性,即可自由扩展属性(null除外)
var obj ={};
obj.a = 100 ;
var arr = [];
arr.a = 100;
function fn(){};
fn.a = 100;
  1. 所有的引用类型,都有一个__proto__属性(隐式原型属性),属性值是一个普通的对象
console.log(obj.__proto__);
console.log(arr.__proto__);
console.log(fn.__proto__);
  1. 所有函数,都有一个prototype属性(显式原型属性),属性值是一个普通的对象
  • Number、String、Boolean、ObjectArrayFunction、Date、RegExp、Error(一定要大写)都是函数
console.log(fn.prototype);
  1. 所有引用类型__proto__属性值指向(完全等===)他的构造函数的prototype属性值
console.log(obj.__proto__ === Object.prototype)
  1. 当试图得到一个对象的某个属性时,若果这个对象本身没有这个属性,那么它的__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.alertName();
  • f本身没有alertName的属性,所以会去f的隐式原型__proto__中去寻找,f的隐式原型__proto__即为其构造函数Foo的显式原型prototype,Foo的显式原型已被扩展了alertName的属性,所以可顺利执行
  • this永远指向对象本身,在执行f.alertName()的时候会执行到第6行alert(this.name),但是这里的this还是f本身

循环对象自身属性

  • 从上述代码中可得f拥有三个属性:name、printName、alertName
  • 但我们往往希望拿到对象本身定义的属性,而不要来自其原型的属性
var item;
for(item in f){
  // 高级浏览器已经在for in中屏蔽了来自原型的属性
  // 但这里建议大家加上这个判断,保证程序的健壮性以满足浏览器的兼容性
  if(f.hasOwnProperty(item)){
    console.log(item)
  }
}

4 原型链

  • f.toString() -> f.__proto__ -> Foo.prototype -> 无toString属性 -> Foo.prototype是一个对象 -> Foo.prototype.__proto__ -> Object.prototype -> f.__proto__.__proto__
  • Object.prototype.__proto__ = null
// 构造函数
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.alertName();
f.toString(); // 要去f.__proto__.__proto__中查找


5 instanceof

  • 用于判断引用类型属于哪个构造函数的方法
  • f instanceof Foo判断逻辑:f__proto__一层一层往上,能否对应到Foo.prototype
  • f instanceof Object判断逻辑:f__proto__一层一层往上,是否对应到Object.prototype

6 题目解答

  1. 如何准确判断一个变量是数组类型
var arr = []
arr instanceof Array //true
typeof arr //object,typeof是无法判断数组的
  1. 写一个原型链继承的例子
  • 面试千万不要这么写
  • 面试写更贴近实战的例子
// 动物
function Animal() {
  this.eat = function () {
    console.log('animal eat');
  }
}
// 狗
function Dog() {
  this.bark = function () {
    console.log('dog bark');
  }
}
Dog.prototype = new Animal();
// 哈士奇
var hashiqi = new Dog();
console.log(hashiqi.eat);
console.log(hashiqi.bark);
// 一个封装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('<p>hello world</p>').on('click', function () {
  alert('clicked');
}).html('<p>javascript</p>')
  1. 描述new一个对象的过程
  • 创建一个空对象
  • this指向这个新对象
  • 执行代码即对this赋值
  • 返回this
  1. zepto(或其他框架)源码中如何使用原型链
  • 阅读源码是最高效提高技能的方式
  • 但不能“埋头苦钻”,有技巧在其中,搜索别人的阅读体会
  • 慕课网搜索“zepto设计和源码分析”
  • 在面试时说出读过源码并分享心得体会十分加分
  • jQuery也可
  • Vue、React不建议现在读
posted @ 2019-02-01 19:30  冯天祥  阅读(222)  评论(0编辑  收藏  举报