二、原型和原型链
Q:1、如何准确判断一个变量是数组类型?
var arr = []; arr instanceof Array; // true //反例 typeof arr;// typeof不能判断arr是否是数组
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('<p>Hello</p>'); // div1.on('click',function(){ // alert('clicked') // }); div1.on('click',function(){alert('clicked')}).html('<p>链式操作</p>') //在之前的函数中增加了return this,由div1调用时便会返回当前对象,即div1,便可以实现链式操作
3、描述new一个对象的过程
a、创建一个新对象
b、this指向这个新对象
c、执行代码,即对this赋值
d、最后返回this
知识点:
(1)、构造函数
function Foo (name, age) { this.name = name; this.age = age; this.class = 'class-2'; // return this;// 默认有这一行 } var f = new Foo('张三',20); //当在执行new Foo()的时候,是先return 空this,然后会赋值this.name = '张三',this.age = 20 console.log(f);//Foo {name: "张三", age: 20, class: "class-1"} console.log(f.name);//张三 console.log(f.age);//20
(2)、构造函数-扩展
var a={}; //其实是var a=new Object()的语法糖 var b=[]; //其实是var b=new Array()的语法糖 function Foo(){...} //其实是var Foo=new Function(...) //使用instanceof判断一个函数是否是一个变量的构造函数
所有的引用类型(对象、数组、函数)都有构造函数,a的构造函数是Object(),b的构造函数是Array(),Foo的构造函数是Function()。所以假如想要判断一个变量是否为数组就可以使用 var a = {}; a instanceof Array // false,更推荐var a = {};这种方式声明一个对象的方式
(3)、原型规则(5条)和示例
a、所有的引用类型(数组、对象、函数),都具有对象特性,即可自由扩展属性(null除外)
var obj={};obj.a=100;// obj可以扩展一个属性a var arr=[];arr.a=100;// arr可以扩展一个属性a function fn(){} fn.a=100;// fn可以扩展一个属性a
b、所有引用类型(数组、对象、函数)都有一个__proto__(隐式原型)
属性,属性值是一个普通的对象
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__)
c、所有的函数,都有一个prototype属性,属性值也是一个普通对象
d、所有引用类型的__proto__
属性值指向它的构造函数的prototype
的属性值
var obj={};obj.a=100; console.log( obj.__proto__ === Object.prototype );//true
e、当试图得到 一个对象的某个属性时,如果这个对象本身没有这个属性,那么回去它的__proto__(即它的构造函数的prototype)中寻找。
// 构造函数 function Foo(name,age) { this.name = name; this.age = age; } Foo.prototype.alertName = function () { alert(this.name); } var f = new Foo('小明'); f.printName = function () { console.log(this.name); } f.alertName();//弹出提示框'小明' f.printName();//小明 f没有alertName属性,于是去f._proto_即Foo.prototype中查找
由对象调用原型中的方法,this永远指向对象本身。
(4)循环对象自身的属性
var item for(item in f){ //高级浏览器已在for in中屏蔽了来自原型的属性 //但是这里建议还是加上这个判断以保证程序的健壮性 if(f.hasOwnProperty(item)){ console.log(item) } }
(6)原型链
// 构造函数 function Foo(name,age) { this.name = name; this.age = age; } Foo.prototype.alertName = function () { alert(this.name); } var f = new Foo('小明'); f.printName = function () { console.log(this.name); } f.alertName();//弹出提示框'小明' f.printName();//小明 f没有alertName属性,于是去f._proto_即Foo.prototype中查找 f.toString();//要去f.__proto__.__proto__中查找
__proto__
属性,且
__proto__
属性值
指向它的构造函数的
prototype
的属性值,所以当f不存在toString时,便会在
f.__proto__
即
Foo.prototype
中查询,而
Foo.prototype
中也没有找到toString。由于
Foo.prototype
也是一个对象,所以它隐式原型
__proto__
的属性值便指向它的构造函数Object的
prototype
的属性值。
![](https://i-blog.csdnimg.cn/blog_migrate/5386f2e80c713c97e21a682e0846efaf.png)
(7)、instanceof
用于判断引用类型属于哪个构造函数的方法 f instanceof Foo
的判断逻辑是f
的__proto__
一层层向上能否对应到Foo.prototype
,再试着判断f instanceof Object