原型和原型链
问题:
-
如何准确判断一个变量是数组类型
-
写一个原型链继承的例子
-
描述new 一个对象的过程
-
Zepto(或其他框架)源码中如何使用原型链
知识点:
1.构造函数
function Foo (name, age) {
this.name = name;
this.age = age;
this.class = 'class-1';
}
Foo
/* ƒ Foo (name, age) {
this.name = name;
this.age = age;
this.class = 'class-1';
} */
var f = new Foo('xiaoming', 12)
f
//Foo {name: "xiaoming", age: 12, class: "class-1"}
建议构造函数的首字母用大写便于阅读。
构造函数会默认return this.
这里讲一下“var f = new Foo('xiaoming', 12)”也就是new一个对象的过程:
首先先传入相应的参数在这个构造函数,然后其实先有一个this,为空对象,再将其进行赋值。像我们前一次讲到的对象是属于引用类型,引用类型可以无限的扩展属性,这里this.name,this.age都可以有点像那个无限扩展属性,给他加上属性,然后赋值,然后最后再返回一个return this。再将这个this传回来,赋值给f.
2.构造函数--扩展
语法糖:
语法糖就是一种便捷写法。在达到相同的目的情况下,更简便的方法。
- var a ={} 其实是var a = new Object() 的语法糖
- var a = [] 其实是var a = new Array()的语法糖
- function Foo(){...} 其实是var Foo =new Function(..)
- 使用instanceof 判断一个函数是否是一个变量的构造函数
对于一个变量的所有东西都有构造函数,对象,函数,数组都是有构造函数的.
对于1,2,3都推荐前者,易读且性能更好
//构造函数拓展
var a = {}
var b = []
function fn (){}
var a = new Object
var b = new Array
fn = new Function (...)
3.原型规则和示例
5条原型规则
原型规则是学习原型链的基础
(1)所有的引用类型(数组,对象,函数),都可以可自由扩展属性(除了null除外)
(2)所有的引用类型(数组,对象,函数),都有一个_proto_属性(简称他隐式原型),属性值是一个普通的对象。
(3)所有的函数,都有一个prototype(显式原型)属性,属性值也是一个普通的对象
(4)所有的引用类型(数组,对象,函数),_proto_属性值指向它的构造函数”prototype ”的属性值
(5) 当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么会去它的_proto_(即它的构造函数的prototype)中寻找。
var obj = {}
obj.a = 100
obj.b = 'abc'
obj
//{a: 100, b: "abc"}
var arr = [1,2,3]
arr.a = 100
arr
//(3) [1, 2, 3, a: 100]
function fn () {}
fn.a = 100
fn.b =200
obj
//{a: 100, b: "abc"}
arr
//(3) [1, 2, 3, a: 100]
fn
//ƒ fn () {}
obj.__proto__
//{constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}
arr.__proto__
//[constructor: ƒ, concat: ƒ, find: ƒ, findIndex: ƒ, pop: ƒ, …]
fn.__proto__
//ƒ () { [native code] }
fn
//ƒ fn () {}
Object
//ƒ Object() { [native code] }
fn.prototype
//{constructor: ƒ}
Object.prototype
//{constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}
obj
//{a: 100, b: "abc"}
obj.__proto__
//{constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}
Object
//ƒ Object() { [native code] }
Object.prototype
//{constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}
obj.__proto__ === Object.prototype
//true
4.原型链
以一个例子来具体来看:
f是 通过构造函数Foo new的一个对象,他有一个自己的方法printName。同时构造函数还增加了一个方法的alertName。
Foo像一个类,而f是他类别下面一个具体的实例。
类的方法他都有,实例还有自己单独的方法。
function Foo(name,age){
this.name = name;
}
Foo.prototype.alertName= function(){
alert(this.name)
}
var f = new Foo('dd',13)
f.printName = function () {
console.log(this.name)
}
f
//Foo {name: "dd", printName: ƒ}
f.printName()
//dd
f.alertName()
f.toString()
//"[object Object]"
f
//Foo {name: "dd", printName: ƒ}
f.__proto__
//{alertName: ƒ, constructor: ƒ}
Object
//ƒ Object() { [native code] }
Object.prototype
//{constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}
f.__proto__.__proto__
/*{constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}
constructor: ƒ Object()
hasOwnProperty: ƒ hasOwnProperty()
isPrototypeOf: ƒ isPrototypeOf()
propertyIsEnumerable: ƒ propertyIsEnumerable()
toLocaleString: ƒ toLocaleString()
toString: ƒ toString()
valueOf: ƒ valueOf()
__defineGetter__: ƒ __defineGetter__()
__defineSetter__: ƒ __defineSetter__()
__lookupGetter__: ƒ __lookupGetter__()
__lookupSetter__: ƒ __lookupSetter__()
get __proto__: ƒ __proto__()
set __proto__: ƒ __proto__()*/
其中含有一个this,this要看具体的情形来判断的,当比如说f.name这个this就是指向f.
4.1 实现一个new的方法
function create() {
let obj = {}
let Con = [].shift.call(arguments)
obj.__proto__ = Con.prototype
let result = Con.apply(obj, arguments)
return result instanceof Object ? result : obj
}
- 创建一个空对象
- 获取构造函数
- 设置空对象的原型
- 绑定
this
并执行构造函数 - 确保返回值为对象
5.instanceof
用于判断引用类型属于哪个构造函数的方法
var arr = [1,2,3]
arr instanceof Array
//true
typeof arr
//"object"
虽然instanceof 不能判断原始类型,我们可以通过改造来实现。
class PrimitiveString {
static [Symbol.hasInstance](x) {
return typeof x === 'string'
}
}
console.log('hello world' instanceof PrimitiveString) // true
// Symbol.hasIntance是一个方法可以自定义判断构造函数是否将对象识别为实例
5.1实现一个instanceof
function myInstanceof(left, right) {
let prototype = right.prototype
left = left.__proto__
while (true) {
if (left === null || left === undefined)
return false
if (prototype === left)
return true
left = left.__proto__
}
}
- 首先获取类型的原型
- 然后获得对象的原型
- 然后一直循环判断对象的原型是否等于类型的原型,直到对象原型为
null
,因为原型链最终为null
6.使用Object.prototype.toString.call(arr) === '[object Array]'
function isArray(o) {
return Object.prototype.toString.call(o);
}
var arr=[2,5,6,8];
var obj={name:'zhangsan',age:25};
var fn = function () {}
console.log(isArray(arr)); //[object Array]
console.log(isArray(obj)); //[object Object]
console.log(isArray(fn)); //[object function]
7.ES5定义了Array.isArray:
Array.isArray([]) //true
问题解决:
-
如何准确判断一个变量是数组类型
instanceof
Object.prototype.toString.call(arr) === '[object Array]'
Array.isArray([]) -
写一个原型链继承的例子
function Elem(id){ this.ele = document.getElementById(id); } Elem.prototype.html = function (value) { var ele = this.ele; if (value) { ele.innerHTML = value; return this; }else { return ele.innerHTML } } Elem.prototype.on = function (type,fn) { var ele = this.ele; ele.addEventListener(type,fn); } var a = new Elem('article'); a.on('click',function() { alert('hello'); }); console.log(a.html());
这里的return this可以使得在后面调用时候,进行循环调用。
-
描述new 一个对象的过程
先传入相应的参数,也可以不传,然后在构造函数里面有一个this的空对象,对他进行扩展属性赋值,再返回一个this。将他返回给那个对象。 -
Zepto(或其他框架)源码中如何使用原型链
可以自行去查找相关资料看一下。