原型 1~3
- 任何函数都有prototype属性,默认指向一个空的Object对象(即原型对象)(只是没有我们要的属性和方法,会有默认的属性方法)
原型对象都有constructor属性,它指向函数对象本身
(构造函数和它的原型对象有相互引用的关系,构造函数的prototype指向原型对象,原型对象的constructor又返回指向构造函数,你中有我我中有你)
function fn() {
console.log('sss')
}
console.log(fn.prototype, typeof fn.prototype) // 'object
console.log(typeof fn.prototype) // 'object'
console.log(fn.prototype.constructor)
//function fn() {xian'shi
console.log('sss')
}
2.给原型添加属性和方法(一般是方法),供实例使用
function Fn() {
}
Fn.prototype.test1= function () {
console.log('I am test1')
}
var fn1 =new Fn()
fn1.test1() //I am test1
3.原型分类:显式原型(prototype)和隐式原型(proto)–prototype、__proto__保存的都是地址
(1)每个函数都有一个prototype属性,默认指向一个空的对象(并不是真的空值,有默认的属性和方法)——显示原型
(2)每个实例对象都有一个__proto__属性——隐式原型
(3)实例对象的隐式原型(proto)的值对应构造函数的显示原型(prototype)
(__proto__在创建实例的时候产生,如this.proto = Fn.prototype)
图解见图1:
function Fn() { // 内部:this.prototype = { }
}
// (1)
console.log(Fn.prototype) // {constructor: ...}
var fn = new Fn() // 内部:this.__proto__ = Fn.prototype
// (2)
console.log(fn.__proto__) // {constructor: ...}
// (3)
console.log(Fn.prototype === fn.__proto__) // true
// 给原型添加方法:
Fn.prototype.test1 = function () {
console.log('hello world')
}
fn.test1() // hello world,通过实例对象的隐式原型找到test
// fn.test1()过程:先在fn的内部去找test1,找到救执行,没有找到-->沿着fn的__proto__(值等于构造函数的prototype)找,一直找到Object
总结:
函数的prototyp属性:在定义函数时自动添加,默认值是空Object对象
实例的__proto__属性:创建实例对象时自动添加,默认值是构造函数的prototype属性值
程序员只能操作显式原型prototype,但不能操作隐式原型__proto__(ES6之前)
原型面试题
function A (){
}
A.prototype.n=1
var b = new A()
A.prototype = {
n:2,
m:3
}
var c = new A()
console.log(b.n,b.m,c.n,c.m) // 1 undefined 2 3
// A.prototype = {…}时,A的prototype重新指向另一块内存,此时已经与前一块内存脱离指向关系,但是前一块内存仍然存在,并且b仍然指向它
原型链 4~5
4.原型链——别名:隐式原型链
访问一个对象的属性(一般是方法)时,先在自身找,找到就返回;
如果没有找到,再沿着__proto__这条链往上找,找到就返回;
如果找到Object的__proto__都没有找到,就返回undefined
这样的一个过程就形成一条原型链。
原型链的作用:查找对象的属性(一般是方法方法)(注意/区别:查找对象的变量是通过作用域和作用于链)
4.1 实例、构造函数、原型三者的关系
实例:proto --> 构造函数:prototype、proto(它是Object的实例所以有__proto__属性) --> 空Object:proto(值为null)、prototype(作为构造函数有这个属性)-constructor–>构造函数
(实例通过__proto__与构造函数、原型联系在一起)
见图3
栗子:分析见图2
function Fn() {
this.test1 = function () {
console.log('I am test1')
}
}
Fn.prototype.test2 = function () {
console.log('U are test2')
}
var fn =new Fn()
fn.test1() // I am test1
fn.test2() // U are test2
fn.test3() // 报错
4.2 特殊情况
(1)函数的显式原型指向的对象:默认的空Object对象(不是真的为空,会有自带一些属性)
特殊情况:Object
function Fn() {
}
console.log(Fn.prototype instanceof Object) // true
console.log(Object.prototype instanceof Object) // false–特殊
console.log(Function.prototype instanceof Object) // true
console.log(Object.prototype) // 不包含方法的空对象
console.log(Fn.prototype) // 包含多个方法的对象
(2)所有函数都是Function的实例(包括Function本身)
console.log(Function.prototype === Function.proto) // true
(3)Object的原型对象是原型链的尽头
console.log(Object.prototype.proto) // null
4.3 原型链的属性问题
读取实例对象的属性时,先在自身对象中查找,找到就返回,不会再继续往原型链上找——看到的效果就是实例对象的属性值覆盖了原型链上的属性值,其实不然!原型链上的属性相当于“备胎”
,当实例对象有该属性时并不会用到原型链上的对应的属性,实例对象的属性值也不会改变(覆盖、重写)原型链上的属性值;
如果没有找到就沿着原型链往上找,如果原型链上有该属性,就返回;
当给实例对象设置属性时,如果该属性与原型链中的属性冲突(重名)时,只会讲该属性名写入自己的属性不会改变原型链上的属性的值。
function Fn() {
}
Fn.prototype.name = ‘pipixi’
var fn1 = new Fn()
console.log(fn1.name) // pipixi
var fn2 = new Fn()
fn2.name = ‘pipijie’
console.log(fn2.name) // pipijie
console.log(fn1.name) // pipixi
注意:一般不会把属性写到原型中,写到原型中的是方法,而属性更多的通过参数传进来。栗子:
function Person(name,age) {
this.name = name
this.age = age
}
Person.prototype.SayHello = function () {
console.log(‘Hello’ + " " + this.name + “!” + "Are you " + this.age + “years old?”)
}
var p1 = new Person(‘lily’, 18)
var p2 = new Person(‘kally’, 39)
p1.SayHello() //Hello lily!Are you 18years old?
p2.SayHello() //Hello kally!Are you 18years old?
建议:属性一般通过构造函数定义在实例对象本身(自用)、而通用的方法写在原型(prototype)上(公用) ——好处:既有可以定义各自的属性、又可以共享通用的方法
原型链面试题
function F() {
}
Object.prototype.a = function () {
console.log(‘I am a’)
}
Function.prototype.b = function () {
console.log(‘I am b’)
}
var f = new F()
f.a() // I am a
f.b() // 报错
F.a() // I am a
F.b() // I am b
5. instanceof探索
A instanceof B ——A是实例对象(有隐式原型属性-proto),B是构造函数(有显式原型属性-prototype)
如果B 的显式原型指向A 的隐式原型链上的某个值时,返回true,否则返回false
栗子1:
function Foo() {
}
var f1 = new Foo()
console.log(f1 instanceof Foo) // true – f1由Foo new出来的,f1肯定是Foo的实例对象呀
console.log(f1 instanceof Object) // true --f1不是Object直接new出来的,但是f1可以通过__proto__原型链找到Object,所以f1也是Object的实例对象
console.log(Object.prototype) // 打印的是new出Objectd的构造函数 function Object
console.log(Object.prototype.proto) // null – 原型链的终点
栗子2:
console.log(Object instanceof Function) // true
console.log(Function instanceof Object) // true
console.log(Object instanceof Object) // true
console.log(Function instanceof Function) //true
function Foo() {
}
console.log(Object instanceof Foo) // false