JavaScript的原型与原型链

1.1 JavaScript的原型与原型链

1.1.1 所有对象都是(构造)函数创建的

所有的对象都是由函数创建!比如:

function Person(name, age) {
   this.name = name;
   this.age= age;
   this.sayName = function () {
       console.log(this.name);
    };
}
var person1 = new Person("peter", 29);
var person2 = new Person("alice", 27);

另一种方式是使用对象字面量表示法。对象字面量是对象定义的一种简写形式,目的在于简化创建包含大量属性的对象的过程。

var person1 = { name: "peter",age: 10};
本质上是:

var person1 = new Object();
person1.name = "peter";
person1.age = 10;

再比如:

var arr = [5, 'x', true];
本质上是:

var arr = new Array();
arr[0] = 5;
arr[1] = 'x';
arr[2] = true;

1.1.2 构造函数与普通函数的区别

构造函数和普通函数没有本质区别,如果一个函数被new的方式调用,则该函数就可以称为构造函数。

构造函数通常:

1.        不需要用return显式返回值的,默认会返回this,也就是新的实例对象。

2.        函数命名建议首字母大写,与普通函数区分开。

但是这不是必须的。

在函数调用的时候:

function fn() { }
构造函数:

1.        f=new fn( );

2.        构造函数内部会创建一个新的对象,即实例f;

3.        函数内部的this指向新创建的实例f;

4.        默认的返回值是实例f。

普通函数:

1.        f=fn( );

2.        在调用函数的内部不会创建新的对象;

3.        函数内部的this指向调用函数的对象(如果没有对象调用,默认是window);

4.        返回值由return语句决定。

 1.1.3 使用new关键字实例化的时候发生了什么?

比如:

var person1=new Person()
1.        第一步,创建一个空对象。

var person1={}

2.        第二步,将构造函数Person()中的this指向新创建的对象person1;

3.        第三步,将person1的_proto_属性指向Person函数的prototype,创建对象和原型间关系;

4.        第四步,执行构造函数Person ()内的代码。

构造函数有return值怎么办?

构造函数里没有显式调用return时,默认是返回this对象,也就是新创建的实例对象。当构造函数里调用return时,分两种情况:

1.      return的是五种简单数据类型:String,Number,Boolean,Null,Undefined。

这种情况下,忽视return值,依然返回this对象。

var fun=function(){
    return 5;
}
var a=new fun();
console.log(a);//显示:fun {},而不是输出5
2.      return的是Object

这种情况下,不再返回this对象,而是返回return语句的返回值。

function Person(name,age){
   this.name=name;
   this.age=age;
    return{job:"waiter"}//返回的是与Person无关的对象!
  }
 varperson=new Person("sheila",10);
 console.log(person.job);//waiter
 console.log(person.name);//undefined
 console.log(person.age);//undefined

1.1.4 所有的函数都有prototype属性(原型)

注意,是函数才有prototype,普通对象没有。函数创建时就自动带有这个属性,也就是我们讲的“原型”,这个prototype的属性值是一个对象,默认的只有一个叫做constructor的属性,指向这个函数本身。


1.1.5 所有的对象都有__proto__

1、所有的对象都有__proto__,指向创建它的函数的prototype注意,你要这样来理解这句话的意思,那就是同一个函数new出来的对象的__proto__都统一指向了这个函数的prototype,根据后面要讲述的原型链规则,也就是说通过这个函数new出来的所有对象都可以直接使用该函数原型上的任意属性和方法!,因此,对于jquery的这种形式就应该能理解了

var $div=$(“div”)
$div.on(“click”,function(){alert(“clicked”);}
$是jQuery的简写别名,其实是一个函数。因此$div是jQuery函数创建的对象,很显然,on方法就是在jQuery.prototype上定义的属性(函数),因此所有jQuery函数创建的对象都已直接使用on方法

2、所有的函数,比如 function fn(){},都是由Function函数创建,因此fn的__proto__指向Function的prototype。

3、比较有意思的是,Function也是函数,因此它也由Function创建的,也就是说它自己创建了自己!所有Function的__proto__指向的就是Function的prototype!

console.log(Function.__proto__ ===Function.prototype) //true

4、同理,Object函数也是由Function创建,因此Object的__proto__同样指向Function的prototype!

console.log(Object.__proto__ ===Function.prototype) //true

5、prototype也是一个对象,原始prototype只有一个叫做constructor的属性,指向这个函数本身。因为prototype是一个对象,因此它也是由Object方法创建,因此它的__proto__将指向Object.prototype,如下所示:

console.log(Function.prototype.__proto__ ===Object.prototype) //true
6、但是 Object.prototype却是一个特例——它的__proto__指向的是null,切记切记!

console.log(Object.prototype.__proto__ === null)//true
因此,根据上面的几条基本概念,从这段简单的代码我们可以画出这样一条关系链图:

// 创建函数Fn,这个函数由Function生成(Function作为构造函数)
function foo() {}
// 创建函数fn,这个函数由Fn生成(Fn作为构造函数)
var fn = new foo()

//fn的__proto__指向其构造函数Fun的prototype
console.log(fn.__proto__ === foo.prototype) //true
//foo的__proto__指向其构造函数Function的prototype
console.log(foo.__proto__ === Function.prototype)//true
// Function的__proto__指向其构造函数Function的prototype,构造函数自身是一个函数,他是被自身构造的
console.log(Function.__proto__ === Function.prototype)//true
// Function.prototype的__proto__指向其构造函数Object的prototype
// Function.prototype是一个对象,同样是一个方法,方法是函数,所以它必须有自己的构造函数也就是Object
console.log(Function.prototype.__proto__ ===Object.prototype) //true
// 与上条相同
// 此处可以知道一点,所有构造函数的的prototype方法的__都指向__Object.prototype(除了....Object.prototype自身)
console.log(foo.prototype.__proto__ ===Object.prototype) //true
// Object作为一个构造函数(是一个函数对象!!函数对象!!),所以他的__proto__指向Function.prototype
console.log(Object.__proto__ ===Function.prototype) //true
// Object.prototype作为一切的源头,他的__proto__是null
console.log(Object.prototype.__proto__ === null)//true

1.1.6 prototype和__proto__小结

1.        所有对象都有__proto__属性,存在继承关系的对象中的__proto__属性构成原型链。

2.        所有函数都有prototype和__proto__属性,函数也是对象,如何区分函数对象和普通对象的标志就是对象是否有prototype属性。

function foo() {}
var fn = new foo()
console.log(typeof foo);//function
console.log(typeof fn);//object
3.        所有函数对象都是Function()构造函数生成的,猜测:函数对象prototype属性是Function()构造函数创建的。函数对象的原型链通常都不长:foo()---Function.prototype---Object.prototype---null;如果继承关系简单,对象的原型链也不长:fn---foo.prototype---Object.prototype---null。

4.        函数对象中是没有成员变量的!永远只有prototype、__proto__等几个固定的属性。比如:

function foo() {
   this.name="peter";
   this.age=10;
}
foo.prototype.job="waiter";//放到原型对象中
var fn = new foo()
得到:

当构造函数foo()被调用时,foo()中的this指针指向的是构造函数将要创建的对象,即未来的对象fn(严格来说,new foo()被执行时,生成对象名为foo,与函数名相同,然后通过赋值语句var fn=new foo()将对象赋值给fn!)。按照规则,构造函数默认返回this,也就是构造函数创建的对象。证明:

function foo() {
   this.name="peter";
    this.age=10;
    console.log(this);//输出:foo { name: 'peter', age: 10 }
    console.log(typeof this);//输出:object
}
foo.prototype.job="waiter";
var fn = new foo()
Javascript中变量是不需要定义的,因此变量都是通过语句“执行”出来的,不是定义出来的,这与C语言是不同的。比如:

this.name="peter";
为this指向的对象创建了一个name变量。而this指向的是fn对象,因此fn对象拥有了name和age成员变量。

再次强调:函数对象没有用户自定义的成员变量,只有固定的几个成员变量,也就是构造函数Function()创建函数对象时有哪些变量就只有哪些变量;对象是可以有用户自定义的成员变量的。

语句:

foo.prototype.job="waiter";//放到原型对象中
在构造函数foo的原型对象中增加了成员变量job,因此fn对象继承了job变量。

1.        函数对象中的prototype似乎只是在构造函数对象中有用,用于指出构造出的对象的“父类”中还有哪些属性可被对象继承。函数对象中的__proto__则没什么用处,函数对象的原型链的上一级永远都是Function.prototype。

2.        Javascript中普通对象更像是一般意义上的对象,有变量成员和函数成员;函数对象更像是函数,即便是构造函数,其有意义的成分主要也就是其中的代码逻辑;构造函数确实就像它的名字,其功能就是为了构造一个对象。
把函数从内部实现上也作为对象来实现或许只是为了“工整”,在设计语言的实现上,只有尽量少的几个概念。当然,Javascript设计者可能设想过,如果只是从用户的角度使用Javascript语言,并不需要知道这些细节,把函数就当成函数,对象就当成对象,构造函数只用于构造对象,并没有什么不可。但是,Javascript语言高手总是会写出“非常规”的代码,让用户没法不去深究其中的本质和细节

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值