【JS面试】第三章 原型与原型链
- 如何准确判断一个变量是数组类型
- 写一个原型链继承的例子
- 描述new一个对象的过程
- zepto(或其他框架)源码中如何使用原型链
1 构造函数
- 构造函数首字母大写
- 构造函数类似于模板
new一个构造函数,返回一个对象的过程
- new的时候把参数传入也可不传
- new函数执行时,创建一个空对象
- this指向这个新对象
this = {}
- 执行代码,即对this.name等开始顺序赋值
- 赋值完后,默认return this
- 赋值给
f
,f.name
、f.age
、f.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条原型规则和示例
- 所有的引用类型(数组、对象、函数),都具有对象特性,即可自由扩展属性(
null
除外)
var obj ={};
obj.a = 100 ;
var arr = [];
arr.a = 100;
function fn(){};
fn.a = 100;
- 所有的引用类型,都有一个
__proto__
属性(隐式原型属性),属性值是一个普通的对象
console.log(obj.__proto__);
console.log(arr.__proto__);
console.log(fn.__proto__);
- 所有函数,都有一个
prototype
属性(显式原型属性),属性值是一个普通的对象
- Number、String、Boolean、Object、Array、Function、Date、RegExp、Error(一定要大写)都是函数
console.log(fn.prototype);
- 所有引用类型,
__proto__
属性值指向(完全等===)他的构造函数的prototype
属性值
console.log(obj.__proto__ === Object.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.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 题目解答
- 如何准确判断一个变量是数组类型
var arr = []
arr instanceof Array //true
typeof arr //object,typeof是无法判断数组的
- 写一个原型链继承的例子
- 面试千万不要这么写
- 面试写更贴近实战的例子
// 动物
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>')
- 描述new一个对象的过程
- 创建一个空对象
- this指向这个新对象
- 执行代码即对this赋值
- 返回this
- zepto(或其他框架)源码中如何使用原型链
- 阅读源码是最高效提高技能的方式
- 但不能“埋头苦钻”,有技巧在其中,搜索别人的阅读体会
- 慕课网搜索“zepto设计和源码分析”
- 在面试时说出读过源码并分享心得体会十分加分
- jQuery也可
- Vue、React不建议现在读