# 原型概念

原型

1.原型是function对象的一个属性,它定义了构造函数制造出的对象的公共祖先。通过该构造函数产生的对象,可以继承该原型的属性和方法,原型也是对象

2.利用原型特点和概念,可以提取共有属性。

3.对象如何查看原型-- >隐式属性__proto_

4.对象如何查看对象的构造函数constructor

Person.prototype   //Person 的原型
Person.prototype = {}  
//是祖先,在函数刚出生的时候就已经被定义好了。
1
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的属性
1
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');
1
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');
1
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');
1
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();
1
2
3
4
5
6
7
8
9
10
11
12
13

# constructor构造器

返回构造该对象的构造函数

function Car() {
}
var car = new Car()
car.constructor--->function Car(){}
1
2
3
4
Car.prototype.abc = '123';
     function Car() {
        }
      var car = new Car();
1
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();
1
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'
1
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;
1
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’
1
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 
1
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();
1
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() {
    }
1
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
1
2
3
4
5

# 模拟创建对象

Person.prototype.name = "sunny";
    function Person() {
}
var person = Object.create(Person.prototype);
1
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()向下取整
1
2
3
4
5
6
7

# call/apply

call/apply

作用,改变this指向。

区别,后面传的参数形式不同。

function test() {
  }
    test()  //------ > test.call(); 
//真正的执行是隐式的test.call()。
//正常写test.call()和test()是一样的
1
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的指向,往后的会当做形参传入实参里面去。
1
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)
1
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;
    }
1
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形参);
//典型的用别的函数的属性来实现自己的方法
1
2
3
4
5
6
7

# apply和call的区别

相同点:

  1. 功能一样 都是改变this指向
  2. call的第一位参数和apply的第一位参数都是this

不同点: 1.call需要把实参按照形参的个数传进去 Wheel.call(this,wheelSize,style);

2.apply需要传一个arguments实参列表数组 Wheel.apply(this,[wheelSize,style]);

#

JavaScript的call和apply方法是做什么的,两者有什么区别?

改变this指向 区别:传参列表不同