Skip to content

JS面试思考:3原型和原型链

从题目入手、思考,寻找知识点最后解答

引子题目

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

知识点

构造函数

js
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) // 创建多个对象

构造函数 - 扩展

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

原型规则和示例

5条原型规则 - 原型规则是学习原型链的基础

  • 所有的引用类型(对象、数组、函数)都具有对象特性,即可自由扩展属性(除null)
js
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__);
//
console.log(fn.prototype)
console.log(obj.__proto__ === Object.prototype)
  • 所有的引用类型都有一个__proto__(隐式原型)属性,属性值是一个普通的对象

  • 所有的函数,都有一个prototype属性,属性值也是一个普通的对象

  • 所有的引用类型(数组、对象、函数), __proto__属性值指向它的构造函数的prototype属性

  • 当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么会去它的__proto__(即它的构造函数的prototype)中寻找

js
// 构造函数
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()

循环对象自身的属性

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

原型链

js
// 构造函数
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__ 中去找

instanceof

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

  • f instanceof Foo 的判断逻辑

  • f的__proto__一层一层往上,能否对应到Foo.prototype

  • 再试着判断 f instanceof Object

解题

如果准确判断一个变量是数组类型

js
var arr = []
arr instanceof Array // true
typeof arr // object, typeof无法判断是否为数组

写一个原型链继承的例子

js
// 动物
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() // 这不是最好的方式

描述new一个对象的过程

  • 创建一个新对象
  • this指向这个新对象
  • 执行代码,即对this赋值
  • 返回this

zepto或其他框架源码中如何使用原型链

阅读源码是高效提高技能的方式,但不能“埋头苦钻”,有技巧在其中

可以在慕课网搜索“zepto设计和源码分析”

写一个封装DOM查询的例子

js
function Elem(id) {
  this.elem = document.getElementById(id)
}
Elem.prototype.html = function(val) {
  var elem = this.elem
  if (val) {
    elem.innerHTML = val
    return this // return this是为了链式调用
  } else {
    return elem.innerHTML
  }
}
Elem.prototype.on = function(type, fn) {
  var elem = this.elem
  elem.addEventListener(type, fn)
}
var div1 = new Elem('div1')
console.log(div1.html())
div1.html('<h1>Hello world!</h1>').on('click', function() {
  alert('html changed...')
})

共 20 个模块,1301 篇 Markdown 文档。