# 原型概念
原型
1.原型是function对象的一个属性
,它定义了构造函数制造出的对象的公共祖先。通过该构造函数产生的对象,可以继承该原型的属性和方法,原型也是对象
。
2.利用原型特点和概念,可以提取共有属性。
3.对象如何查看原型-- >隐式属性__proto_
4.对象如何查看对象的构造函数constructor
Person.prototype //Person 的原型
Person.prototype = {}
//是祖先,在函数刚出生的时候就已经被定义好了。
2
3
# 原型总结
总结
Person.prototype.name ="hehe"
他自己给自己一个属性name,值hehe。
他的后代构造函数person可以调用.name属性
,但是构造函数person本身没有name属性和值,原型就是可以让后代(构造函数产生的对象继承该原型的属性和方法)
# 例子 继承
function Person() {
}
var person = new Person();
var person1 = new Person();
// person和 person1 构造函数都可以继承Person.prototype的属性
2
3
4
5
# 例子 查找顺序
Person.prototype.Lastname ="Deng"
Person.prototype.say = function () {
console.log('hehe');
}
function Person(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
var person = new Person('xuming',35,'male');
2
3
4
5
6
7
8
9
10
11
解析
person.name是xuming
,因为如果构造函数里面写了的属性,原型上也定义了属性,他会按照先寻找构造函数里面的属性来寻找,(就近原则)
person.Lastname "Deng";
person.say "hehe";
构造函数对象上没有的东西 会去原型上找
# 车间例子
把公有属性提取出来写到原型上,函数执行只执行一遍。
Car.prototype.carname = "BMW";
Car.prototype.height = 1400;
Car.prototype.lang = 4900;
function Car(color,owner) {
this.color = color;
this.owner = owner;
}
var car = new Car('red','prof.ji');
var car1 = new Car('green','deng');
2
3
4
5
6
7
8
9
把几个构造函数想共有的属性全部写到原型上
, 这样的话构造函数想调用也可以调用,因为原型初始就一个一个空的对象。
car 和 car1都有相同的属性在原型上,继承了原型的属性。
# 原型的增删改查
Person.prototype.lastName = "Deng"
function Person(name) {
this.name = name;
}
var person = new Person('xuming');
2
3
4
5
原型增删改查
增 Person.prototype.lastName =
删 delete Person.lastName
可以删除,但是删除的是自己的 lastname,自己没有。不会报错,原型上的 lastname 还在
改 Person.lastName = "james"
查 Person.lastName -->Deng
改完person.lastName-->james
改完的是自己构造函数内部的lastName,(也就是给构造函数 Person增加了 lastname 属性)跟原型没关系
,原型Person.lastName还是Deng
如果想修改原型的属性,除非写Person.prototype.lastName =
# 原型写法
Car.prototype.carname = "BMW";
Car.prototype.height = 1400;
Car.prototype.lang = 4900;
//因为原型也有对象(类似于一个空对象)
//所以可以将原型的属性全部写在对象的里面。
Car.prototype = {
carname = "BMW";
height = 1400;
lang = 4900;
}
function Car() {
}
var car = new Car();
2
3
4
5
6
7
8
9
10
11
12
13
# constructor构造器
返回构造该对象的构造函数
function Car() {
}
var car = new Car()
car.constructor--->function Car(){}
2
3
4
Car.prototype.abc = '123';
function Car() {
}
var car = new Car();
2
3
4
car.prototype
里面有一个constructor
和__proto__
car.constructor
可以查看构造他的函数名字--->function Car() {}
constructor可以手动更改
提示
proto,constructor 属性是对象所独有的。
prototype 属性是函数独有的。
函数也是对象的一种
,那么函数同样也有属性__proto__,constructor
# 更改constructor指向
function Car() {
}
function Person() {
}
Car.prototype = {
constructor : Person
}
var car = new Car();
2
3
4
5
6
7
8
提示
因为car的constructor
原本是Car函数构造的
,通过手动将他的构造器更改为Person
,那么他的内部就成为了person
。(认贼作父),也继承了 Person 函数里面的属性。
Person.prototype.name = 'abc';
function Person() {
//new来构造对象的话里面发生了三个隐式
// var this = {
// __proto__:Person.prototype
// __proto__里面存的就是原型
// }
}
var person = new Person();
//控制台里输入person 会展示person的属性和属性名
// 返回Person 里面有__proto__属性
// __proto里面还有属性: constructor:Person()
// __proto__:Object
// name:'abc'
2
3
4
5
6
7
8
9
10
11
12
13
14
15
访问步骤:访问person.name 会先上自己的原型上去找有没有name属性,没有的话会去Person的__proto__里面去找,然后就是Person.prototype。值为 'abc'
# 改变proto的指向
Person.prototype.name = 'abc';
function Person() {
//new来构造对象的话里面发生了三个隐式
// var this = {
// __proto__:Person.prototype
}
}
var obj = {
name: "sunny"
}
var person = new Person();
person.__proto__ = obj;
2
3
4
5
6
7
8
9
10
11
12
提示
再次访问person.name 也会继续去找自己内部的__proto__ 此刻内部的__proto__已经改成了obj,所以输出的是sunny。
# 例子
Person.prototype.name = 'sunny';
function Person() {
}
var person = new Person();
Person.peototype.name = 'cherry';
//访问 person.name
//为cherry 因为把它的prototype最终改为了name = ‘cherry’
2
3
4
5
6
7
# 另一种写法
person.prototype.name = 'sunny';
function Person() {
//var this = {__proto__:Person.prototype--->sunny}
}
var person = new Person();
//此刻Person.prototype换了一个空间
//但是person的proto指向的还是最上面的prototype
Person.prototype= {
name: 'cherry'
}
//访问 person.name
//为sunny 因为它的prototype第一次指向的就是sunny
2
3
4
5
6
7
8
9
10
11
12
推导
var obj = {name : "a"};
var obj1 = obj;
obj = {name : "b"};
此刻访问obj.name 为b, 访问obj1.name 为a
可以转换为原型理解
Person.prototype = {name:"a"};
proto = Person.prototype;
Person.prototype ={name: "b"};
# 原型链
# 如何构成原型链
Grand.prototype.lastName = "grandfather";
function Grand() {
}
var grand = new Grand();
Father.prototype = grand;
function Father() {
this.name = 'father'
}
var father = new Father();
Son.prototype = father;
function Son() {
this.hobbit = "smoke";
}
var son = new Son();
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
把这一流程叫做原型链,原型链的连接点就是proto,原型链的访问顺序跟作用域链的访问顺序差不多,都是可近的来。
# 原型链增删改查
原型链上的增删改查
查: 就近原则的查
删: 通过它的子孙是无法删除的,通过自身,也就是自己的原型是可以删除的 delete Father.prototype
修: 同删除一样,只能自己修改自己
增 :同删除一样,只能自己给自己加
原型链的顶端是object.prototype
字面量形式创建对象 var obj = { }
;---- > new Object();
此类写法跟var obj = new Obeject()
写法一样,系统会隐式创建new Object();
function Person() {
}
2
Person.prototype = { } --->原型是object.prototype 它有一个默认的prototype,默认的prototype就是一个字面量形式创建的对象 所以原型也是object.prototype
# 创建对象
object.create(原型)
也能创建对象
var obj = {name : "sunny",age: 123};
var obj1 = object.create(obj)
//现在obj1也是一个对象
//obj1的原型就是obj
//所以现在obj1.name就是sunny
2
3
4
5
# 模拟创建对象
Person.prototype.name = "sunny";
function Person() {
}
var person = Object.create(Person.prototype);
2
3
4
结论
绝大多数对象的最终都会继承自object.prototype 只有undefined和null没有tostring方法
# 小bug
0.14*100 打印结果会出现小偏差
JavaScript 精度不准导致的。
for(var i = 0; i < 10; i++) {
var num = Math.floor(Math.random() * 100);
console.log(num);
}
//Math.random()会生成0-1之间的小数
//Math.floor() 取整(向下取整)
//Math.ceil()向下取整
2
3
4
5
6
7
# call/apply
call/apply
作用,改变this指向。
区别,后面传的参数形式不同。
function test() {
}
test() //------ > test.call();
//真正的执行是隐式的test.call()。
//正常写test.call()和test()是一样的
2
3
4
5
# call/apply 应用
function Person(name,age) {
//this = obj
this.name = name;
this.age = age
}
var person = new Person('deng',100);
var obj = {
}
Person.call(obj);
//obj传进去,它会让Person里面的所有预设的this全部都变成obj
//不new person的话,this默认指向window
//如果想要传入参数的话,给call(obj)里面加 ,'cheng',300。
//控制台打印obj就有了name cheng和age 300的 属性和值。
// 因为call的参数 第一个 会改变this的指向,往后的会当做形参传入实参里面去。
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# call实战例子
借用另一个函数实现自己的功能
function Person(name,age,sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
//Person的实参变成了Student的形参再放到Student的实参里面去
//利用了person的方法,实现了自己功能的封装。
function Student (name,age,sex,tel,grade) {
//此处的this 就相当于
// var this = {name: "",age: "",sex: ""}
//把Person的this传到Student里面去
Person.call(this,name,age,sex);
//Person.call 一定调用Person函数执行 第一个里面传进去的是this
//call写到这里就相当于把Person的属性放到了这里
//然后调用自己剩下的的两行
this.tel = tel;
this.grade = grade;
}
var student = new Student('sunny',123,'male',139,2017)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# call实战例子2
造车工厂
function Wheel(wheelsize,style) {
this.wheelsize = wheelsize;
this.style = style;
}
function Sit(c,sitcolor) {
this.c = c;
this.sitcolor = sitcolor;
}
function Model(height,width,len) {
this.height = height;
this.width = width;
this.len = len;
}
2
3
4
5
6
7
8
9
10
11
12
13
提示
上面这三个函数是车间不同的功能和分配,如果想要在下面车厂实现的话用call
function Car(wheelsize,style,c,sitcolor,height,width,len) {
Wheel.call(this,wheelSize,style);
Sit.call(this,c,sitcolor);
Model.call(this,height,width,len);
}
var car = new Car(所有Car形参);
//典型的用别的函数的属性来实现自己的方法
2
3
4
5
6
7
# apply和call的区别
相同点:
- 功能一样 都是改变this指向
- call的第一位参数和apply的第一位参数都是this
不同点: 1.call需要把实参按照形参的个数传进去 Wheel.call(this,wheelSize,style);
2.apply需要传一个arguments实参列表数组 Wheel.apply(this,[wheelSize,style]);
# 题
JavaScript的call和apply方法是做什么的,两者有什么区别?
改变this指向 区别:传参列表不同