关于js原型链的一个问题

function person(params) {
  console.log(this.fs);  
}

person.prototype.say = function () {
  console.log(this);
};
Function.prototype.say = 'say'
person.say; // say

person();

var a = {}

a.prototype.hand = function () {
  console.log('haha');
}
a.hand(); // throw error, why?

lz当然知道没有实例化,但问题主要是这两个:

  1. 这里为什么不会沿着原型链向上找呢? 为什么实例化之后就能从原型链上找,未实例化就不行?
  2. 同时,我们打出来 person.prototype 是能看到它的原型的。 我同时又定义了 Function.prototype.say = 'say' 之后, person.say 就有了结果? 为什么不读取自己的 prototype 而是读取 constructor 的 prototype呢?
阅读 5.5k
12 个回答

var person = new Person()//要实例化对象(另外类名一般要大写)
person.say()

function person(params) {
  console.log(this.fs);  
}

person.prototype.say = function () {
  console.log(this);
};
Function.prototype.say = 'say'
person.say; // say

原型链的查找是通过__proto__属性往父类查找,实例的 __proto__属性指向父类的prototype属性。
当你调用实例的属性或者方法时,首先找对象自己的属性,没有再根据__proto__属性往上找到父类的prototype对象中属性,一直往上直到Object.prototype,最后因为 Object.prototype.__proto__ === null 就over了。

现在看你上面的例子person是一个函数,所以它是Function类的实例,也就是说person.__proto__ === Function.prototype,当你获取属性 say时,person函数对象自己没有这个属性(注意:person.prototype上的属性 不是person函数对象自己的哦),然后就找person.__proto__指向的对象,所以找到了为 say 字符串。
为什么不是console.log(this)呢? 因为查找链是根据__proto__查找的,person.prototype上的方法因该由person的实例,也就是new person()来根据__proto__属性找到。

楼主有这些疑问的关键是,用Java等传统的面对对象的编程语言的思想来看JavaScript的对象创建和继承,这是错误的。JavaScript中一切皆对象,本质上没有类的概念,实例化、构造函数这些模拟传统面对对象编程语言的概念,只会让你更加看不清楚JavaScript。先把这些概念忘掉。至于你的两个问题:

1.第一个问题:a.hand(); // throw error, why?
因为a是一个字面量对象,是没有prototype属性的,只有函数才有这个属性,所以你定义

a.prototype.hand = function () {
  console.log('haha');
}

时已经会抛出错误,只不过因为你后面还有其他语句,这一句的异常被控制台吃掉了,如果这一句是最后一句的话,你能看到下面的异常:

clipboard.png

字面量对象的原型是Object.prototype,所以如果改成下面的写法就对了:

var a = {}

Object.prototype.hand = function () {
  console.log('haha');
}
a.hand(); // haha

2.第二个问题

这里你搞混了一个概念,一个对象的原型并不是[对象].prototype,而是[对象].__proto__。prototype只不过是函数的一个特殊属性,它指向了new 这个函数创造出来的对象的原型。所以person这个函数的原型是person.__proto__, 指向的就是Function.prototype,所以person.say会读取到
Function.prototype.say。person.prototype 是 new person()创建出来的对象的原型,不是person这个函数对象的原型。

正好刚写了一篇这方面的文章,可以帮助你理解:https://segmentfault.com/a/11...

其实你是混淆了“原型链”跟“原型对象”。

原型链是由 __proto__ 构成的,查原型链其实就是顺着 __proto__ 查。

prototype 只是函数的一个属性而已,在 new 的时候会赋予实例对象。

Prototype 与 Proto 的爱恨情仇

问题1:这里person还是构造函数,不是一个对象,person.say()这样是错误的。
问题2:var a = {},这里a是对象,而不是函数,只有函数才有prototype属性,默认有的是__proto__

楼主明显将 类与实例以及原型链之间的关系搞错了.
建议将他们分别打印出来对照看。

person.prototype.say = function () {
  console.log(this);
};

上面等价于在person的原型对象上添加say方法,这个方法被所有类的实例所共享。但是在类上是无法访问的。
除非这样写:

person.say = function () {
  console.log(this);
};

person.say(); 当然出错了,你say方法是定义到person原型对象上的,不是person上,想要访问say方法可以这样:

person.say=function (){...do something}; //可以person.say(); 
var person1 = new person();
person1.say(); //这样也行 

我这个人太俗了!说不来,翻个书给你看看!

图片描述

框的部分多看几遍。
参考《JavaScript高级程序设计》P:148

这里注意下几个东西
Object, Function, function, 实例;

这里区分大小写!

// 1
function person(params) {
  console.log(this.fs);  
}

// 2
person.prototype.say = function () {
  console.log(this);
};
//3

Function.prototype.say = 'say';

//4
person.say; // say

//5
person();

// 6
var p = new person();

// 7
p.say();

// 8
var a = {}

// 9
a.prototype.hand = function () {
  console.log('haha');
}
//10
a.hand(); // throw error, why?

1) 注意这里 function 可以理解为字面量, 它是用来实例化一个Function类型,等效于 var person = new Function()。
2) Function类型的所有实例都会有默认的prototype属性,可以给其添加键值队属性,其效果就是若把person当成构造函数时,会在其输出实例中添加此属性。(表达有点漏洞)
3) 效果是在Function的实例中添加一个属性,其效果就是person会有添加一个say的属性,eg,默认添加的有call,apply,bind等。
4)person是Function的实例,调用的方法指向3), 参考 person.call()
5) 调用时如果当成 普通函数 来使用时,person就是一个 普通函数, this的指向是指向调用时的上下文。
6) 但如果是当成 构造函数 使用时,this就指向返回的对象(p),
7) 此时p.say是一个属性访问的过程,其指向的是say函数实例,也是一个调用过程,但此时调用的时候,上下文是 (p)。
8) Object类型实例化的字面量方式,效果等同于 var a = new Object(); Object 本身是具备prototype 属性的,因为他是一个Function类型。(Function本身是Object类型, Object也是Function类型的,bject类型的实例是个普通对象,这一理解会比较简单),
9)
10) 把8,9,10 独立拿出来看,不要绕到原型对象和原型链里面去,这里还没到哪儿去~

Object.prototype.hand = function() {};

这样,每个Object实例都加上去了,其实也是常用的polyfill的手段~!

构造函数首字母大写 function Person(){},创建实例的话,var person = new Person();这个person是一个对象。函数才有prototype对象,其中prototype包含constructor和__proto__;对象才有__proto__

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏