定义:
原型是function对象的一个属性,它定义了构造函数制造出的对象的公共祖先。通过该构造函数产生的对象,可以继承该原型的属性和方法。原型也是对象。
//Person函数是对象
//Person.prototype -- 原型(在被构造函数定义时就产生了)
//Person.prototype = {} 是祖先(它也是一个对象)
Person.prototype.LastName = "Wei";//原型的一个属性
Person.prototype.say = function(){
console.log("hehe");
}//原型的一个方法
function Person(){
//Person中LastName和say,但是继承了祖先的属性和方法
}
var person = new Person();
var person1 = new Person();
//person 和 person1 都继承了该原型的属性和方法
console.log(person.LastName);//得到"Wei"
function Person(name,age){
this.name = name;
this.age = age;
}
var person = new Person("junjie",19);
//一个正常的对象有自己的属性和方法也有继承而来的属性和方法
//原型的一般用法:转移构造函数中一些固定的属性和方法到原型中,减小耦合
利用原型特点和概念,可以提取共有属性。
对象如何查看原型–>隐式属性 __proto__
Car.prototype = {
LastName : "WEI",
}
function Car(name){
this.name = name;
}
var car = new Car("junjie");
console.log(car.__proto__);
//结果是原型的对象,点开里面有constructer:f Car(name)和__proto__
//可以通过命明规则来说明自己私人的属性,比如 var _pravate=~;
//过程:在用new构造对象的时候,发生下面过程
//在Car 里面 var this = { __proto__ : Person.prototype};
//这里的作用相当于连接原型和构造函数
//每一个新产生的car中都有__proto__属性来放原型
//在查找属性LastName时,car会先在Car中查找,然后顺着__proto__再在Car.prototype中查找
//同样可以用于修改, car.__proto__ = obj{LastName : "Wang"}
注意
Person.prototype.name = "sunny";
function Person(){
//var this = {__proto__:Person.prototype}
}
var person = new Person();
Person.prototype = {
name : "cherry"
}
console.log(person.name);
//注意这里得到的结果是"sunny"而不是"cherry"
//因为在声明新对象person的时候,它的__proto__连接的是原来的Person.prototype
/* 过程理解大致如下:
首先是预编译,提升变量person和function Person
开始执行。先给Person的原型增加新属性name : "sunny"
然后person = new Person(),person变成一个对象
person.__proto__里面的存的是 obj={name:"sunny",constructor:Person()}
下一步执行Person.prototype={name:"cherry"}
因为person的proto属性早就被定义好了
所以这里的修改该的是原型的属性,而不是person中的__proto__,
person在自身属性找不到name时,会在__proto中查找;
简化流程:
Person.prototype = {name : "sunny"}
person.__proto__ = Person.prototype;
Person.prototype = {name : "cherry"}
*/
//注意顺序会影响结果,如果将6~8行代码,放在peoson = new Person()之前
//结果就是"cheery"
独立生成对象如何查看对象的构造函数 --> constructor
function Car(){
}
var car = new Car();
console.log(car.constructor);
//这里的结果会显示构造函数,说明原型中有这个constructor属性,这是系统自动加的
/*
原型中Car.prototype = {
constructor:Car(),
_proto_:Object
}
*/
//这时候可以改变constructor指向
//一种方式:Car.prototype.constructor = Person;
//另一种方式:Car.prototype = {constructor : Person}
console.log(car.constructor);
//得到的是 Person(){}
如何构成原型链?
//原型的__proto__属性,Grand.prototype.__proro__等于Object.prototype
//Object.prototype是所有对象的最终原型
//访问Object.prototype.__proto__得到null,说明这是终端
Grand.prototype.lastName = "Deng";
function Grand(){
this.name = "daming";
}
var grand = new Grand();
Father.prototype = grand;//grand是构造函数Father的原型
function Father(){
this.name = "xiaoming";
}
var father = new Father();
function Son(){
this.name = "xiaoxiaoming";
}
Son.prototype = father;//father是构造函数Son的原型
var son = new Son();
console.log(son.lastName);
/*这里通过原型链访问,son-->son.__proto__(即father)
-->father.__proto__(即grand)-->grand.__proto__,lastName
有点类似作用域链
原型链上属性的增删改查
一般子不能修改父,因为只会在自身上面增加新属性
//沿用上例
function Father(){
this.name = "xiaoming";
fortune = {
card1 : "visa",
card2 : "master"
}
}
//访问son.fortune可以看到父元素的fortune对象
son.fortune = {card1:"mi",card2:"huawei"};
console.log(father.fortune);//这里father中的fortune对象没改
//变的是son中增加了对象fortune,里面含两个属性
//但是以下方式可修改(忽略第10 11行代码)
son.fortune.card2 = "mi";
//因为fortune后面跟属性,可以说forture是引用值
//这里相当于son访问的是引用值fortune的card2这个原始值
//沿用上例
function Father(){
this.num = 100;
}
son.num++;
//结果是son中增加了一个新属性num 值是101,而father中没有变
this的归属问题
Person.prototype = {
name : "a",
sayName : function(){
console.log(this.name);
}
}
function Person(){
name : "b";
}
var p1 = new Person;
p1.name = "c";
console.log(p1.sayName());
//这里的检索过程应该是
//p1-->p1.__proto__(即Person.prototype)
//找到函数体后执行,this.name
//这时候的this指的是p1,因为是p1过来调用的
//所以结果是"c",如果没有第37行代码,结果就是"b"
//因为name = "b" 是所有新造对象都共有的
绝大多数对象的最终都会继承自身Object.prototype
var obj1={}//这是一个自变量对象的创建
//其实质是调用了系统函数即 var ovj1 = new Object();
//但一般都不用调用系统函数的写法。
//obj1.__proto__ ----> Object.prototype
//绝大多数对象的最终都会继承自身Object.prototype
//例外(联系下面的Object.create())
//var obj = Object.create(原型);
var obj = {name : "sunny" , age : 123};
var obj1 = Object.create(obj);
//obj1是一个对象,它的原型是obj
//利用create将对象的构造函数的原型中的属性提给对象
Person.prototype.name = "sunny";
function Person(){
}
var person = Object.create(Person.phototype);
//Object.create()中的括号不能不填,不填会报错
//报错说里面必须是 对象(可以是数组) 或者 null
//如果填入了 null,这是构造出来的对象是没有原型的
var person = Object.create(null);
console.log(person.toString());
//toSpring是Object.prototype中的一个方法,是显示类型转换的系统函数
//这里报错会说没有
person.__proto__ = Object.prototype;
console.log(person.toString());
//这里人为的加入Object.prototype是没有用的.
toString拓展
console.log(ture.toString());
//得到"ture"
console.log(123.toString());
//这种调用是错误的,因为123.中的“.”会当成浮点型
//要掉用需要一个变量存123
方法的的重写
var num = 123;
console.log(num.toString());
//-->因为num是原始值,所以会用到包装类 new Number(num).toString();
/*
Number.prototype.toString = function(){
//这里是Object中的方法的 "重写"
//Number的原型中是自带toString的,这里不是Object的toString
return "hehe";
}
*/
Number.prototype.toString = function(){
return "h1h1";
}
var k = 123;
console.log(k.toString());
//此处返回了h1h1
作用 :
改变this指向
function Person(name,age){
this.name = name;
this.age = age;
}
var person = new Person("deng",100);
var obj = {}
//一般来说执行一个函数test()相当于teat.call(),call是一个方法
Person.call(obj,"cheng",300);
//这里让Person中所有的this都变成obj,同时传入参数
/*得到
obj = {name:"cheng",age=300}
//实质:利用call来给obj按Person工厂来制作属性
实现过程(借用别人函数实现自己功能)
function Person(name,age,sex){
this.name = name;
this.age = age;
this.sex = sex;
}
function Student(name,age,sex,tel,grade){
this.name = name;
this.age = age;
this.sex = sex;
this.tel = tel;
this.grade = grade;
}
var student = new Student("wei",19,"male",137,2020);
/* 用法:
将第7~9行变为 Person.call(student,name,age,sex);
但是上面这个具有唯一性,只针对student
所以要改为 Person.call(this,name,age,sex);
*/
function Student(name,age,sex,tel,grade){
Person.call(this,name,age,sex);
this.tel = tel;
this.grade = grade;
}
apply和call区别不大
区别
后面传的参数形式不同 。
call 需要把实参按照形参的个数传进去
apply 只能传一个参数,是数组
Person.apply(this,[name,age,sex]);
两者功能相同,都是改变this指向,但传参列表不同。
传统继承–>原型链
过多的继承了没用的属性
//参考上方原型链中的祖先 父亲 后代
借用构造函数(沿用上例call)
不能继承借用构造函数的原型
Student的原型仍然是Student.prototype
每次构造函数都要多走一个函数(操作上和运行上一般般)
因为call每次构造函数都要多走一次Person函数
原型共享(比较好)
Father.prototype.lastName = "Deng";
function Father(){
}
function Son(){
}
Son.prototype = Father.prototype;
var son = new Son();
var father = new Father();
//封装一个函数实现继承
//继承的两中语法
//function extend(){}
function inherit(Target,Origin){
Target.prototype=Origin.prototype;
}
//表示Target继承至Origin,传进来的应该是构造函数
/*
这里要注意顺序的问题:
要先执行封装函数,再赋值son= new Son();
否则不生效
*/
圣杯模式
(方法仍然是公有原型)
Father.prototype.lastName = "Deng";
function Father(){
}
function C(){
}
function Son(){
}
C.prototype = Father.prototype;
//Father.prototype是Father和C的公有原型
Son.prototype = new C();
//此处通过原型链的方式来形成关系,Son可通过原型链爬到Father.prototype
//当增加Son.prototype.qq增加到的是C中
/*封装函数:
function inherit(Target,Origin){
function F(){};
F.prototype = Origin.prototype;
Son.prototype = new F();
Target.prototype.constructor = Target;
//修正信息用
Target.prototype.uber = Origin.prototype;
//此处用于说明继承的对象是谁
}
*/
//高端的写法
/*
var inherit = ( function (){
var F = function(){};
return function(targer,Origin){
F.prototype = Origin.prototype;
Son.prototype = new F();
Target.prototype.constructor = Target;
Target.prototype.uber = Origin.prototype;
这里是利用了闭包封装私有化的思想,F函数体存于圣杯函数
的作用域中使用,成为圣杯函数的私有化变量,因为F是隐式的
附加的东西,所以这是一个很好的方法
}
}
());
*/
管理变量,防止污染全局,适用于模块化开发
一个文件里面声明的变量可能与另一个文件中变量相同,导致覆盖。4
解决办法:命明空间
<script type="text/javascript">
var org = {
departmeng1:{
jicheng:{
name : "abc",
age : 123
},
xvsong:{
}
},
departmeng2:{
zhangsan:{
name : "kkk",
age : 4
},
xvgao:{
}
},
}
var jicheng = org.department1.jicheng;
//通过闭包实现私有化
//姬成课程13,后面补充
</script>
如何实现链式调用模式 (模仿jquery)
var deng = {
smoke : function (){
console.log("Smoking");
}
drink : function (){
console.log("drinking");
}
perm : function (){
console.log("preming");
}
}
deng.smoke().drink();
//这里无法连用,因为smoke返回的是undefined
//所以只需要在3-4行代码中间加个 return this;这里的this就是deng
obj.eat().smoke().drink().eat().sleep();
var obj = {
name:"abc"
}
//每次访问obj.name ----> 隐式转化位obj["name"]
//另一种属性表示方法,注意要放"name"不是变量name
//后者更灵活,因为可以利用字符串拼接
var deng = {
wife1 : {name : "xiaoliu"},
wife2 : {name : "xiaozhang"},
wife3 : {name : "xiaohong"},
wife4 : {name : "xiaoqi"},
sayWife : function (num){
return this["wife" + num]
; }
}
//目的:遍历一个对象,知道里面的属性值
var obj = {
name : "123",
age : 123,
sex : "male",
height : 180,
weight : 75
}
//forin循环
for(var prop in obj){
console.log(prop + " " + typeof(prop));//都是字符类型
}
//说明检索的属性是字符
//prop相当于一个变量,存属性名,每一圈循环都不一样,prop名字可自定义
var obj = {
name : "123",
age : 123,
sex : "male",
height : 180,
weight : 75,
}
for(var prop in obj){
console.log(obj.prop);
}
//这里得到的都是undefined
//obj.prop ----> obj["prop"]
//说明在obj中检索"prop"这个字符串,这时的prop不是一个变量
var obj = {
name : "123",
age : 123,
sex : "male",
height : 180,
weight : 75,
prop : 234
}
for(var prop in obj){
console.log(obj.prop);
}
//这里得到的六个结果都是234
//更正方法obj.prop改为obj.[prop]
var obj = {
name : "123",
age : 123,
sex : "male",
height : 180,
weight : 75,
__proto__ : {
lastName : "deng"
}
}
for(var prop in obj){
console.log(obj.[prop])
}
//这时候也会把原型中的lastName显示出来,但我们的目的是让它不显示
//利用obj.hasOwnProperty(prop)这个方法返回布尔值这个方法来实现
for(var prop in obj){
if(obj.hasOwnProperty(prop)){
console.log(obj.[prop]);
}
}
//prop会延展到原型,但遇到终端Object.prototype时不会遍历它
//但是如果手动设置Object.prototype = {abc:"123"}就会显示
var obj = {
name : "123",
age : 123,
sex : "male",
height : 180,
weight : 75,
__proto__ : {
lastName : "deng"
}
}
Object.prototype.abc = "123";
for(var prop in obj){
if(!obj.hasOwnProperty(prop)){
console.log(obj.[prop]);
}
//这时候得到结果是 "deng" 和 "123"
//小结:prop不会遍历到系统的原型
//用来看属性是不是属于对象的,注意前面是要写字符串类型的属性名
"height" in obj
//得到true
//但缺点在于"lastName" in obj得到也是obj
//所以in只能判断这个对象上面能不能访问到这个属性
//并不是判断这个属性属不属于这个对象
instanceof(重要,常考)
function Person(){
}
var person = new Person();
//A instanceof B
//A 是不是 B构造函数构造出来的
/*
person instanceof Person 这里得到的是true
但是 person instanceof Object 也是true
//数组也是对象,[]是空数组
[] instanceof Array 是true
但是[] instanceof Object 也是true
总结:看A对象的原型链上 有没有 B的原型
它可以解决的一个比较强大的问题
var arr = [] || {}
//注意:对象和数组的typeof都是object
//现在 如何判断arr是数组还是对象
/*①
if(arr instanceof Array){
//这是一个数组
}
*/
/*②
数组因为也是对象,所以也有constructor
console.log([].constructor);
这里得到 function Array{[native code]}
而对象的constructor是function Object{[native code]}
*/
/*③
用法:Object.prototype.toString.call([]);
控制台得到[object Array]
Object.prototype.toString.call(123);
控制台得到[object Number]
Object.prototype.toString.call({});
控制台得到[object Object]
原因
下面是一个方法
Object.prototype.toString = function(){
//里面有this
//谁调用this就是谁
//识别this,返回相应的结果
//这里的this是数组
}
*/