一、原型prototype

  • 原型prototype它是function对象上的的一个属性
  • 把它打印出来会发现它是一个对象
  • 每一个函数都有prototype,在prototype上有一个constructor属性,存储的是当前构造函数本身

    1. // 原型:prototype是function对象的一个属性
    2. // 将其打印出来的时候我们会发现它是一个对象
    3. // 每一个函数都有prototype,在prototype上有一个constructor属性,存储的是当前构造函数本身
    4. function Handelphone(){}
    5. console.log(Handelphone.prototype);
  • 原型prototype是定义构造函数构造出的实例对象的公共祖先

  • 所有被该构造函数构造出的实例对象都会继承原型上的属性和方法
  • 实例化的对象在寻找属性或者方法的时候,首先看自己的构造函数上有没有,有的话,就优先使用自己的,没有会基于proto向原型上查找

    1. // 原型prototype是定义构造函数构造出的实例对象的公共祖先
    2. // 所有被该构造函数构造出的实例对象都会继承原型上的属性和方法
    3. // 实例化的对象在寻找属性或者方法的时候,首先看自己的构造函数上有没有,有的话,就优先使用自己的,没有会基于__proto__向原型上查找
    4. function Handelphone(color, brand){
    5. this.color = color;
    6. this.brand = brand;
    7. this.screen = '18:9';
    8. this.system = 'Android';
    9. }
    10. Handelphone.prototype.rom = '64G';
    11. Handelphone.prototype.ram = '6G';
    12. Handelphone.prototype.screen = '16:9';
    13. var hp1 = new Handelphone('red', '小米');
    14. var hp2 = new Handelphone('black', '华为');
    15. console.log(hp1.ram);
    16. console.log(hp2.rom);
    17. console.log(hp1.screen);
    18. console.log(hp2.screen);
  • 上面的代码优化

  • 在封装插件化的时候,一般都是将方法写在原型上,有固定值的属性也要写在原型上;只有需要传递参数的配置项才写在构造函数上

    1. // 代码优化一
    2. // 在封装插件化的时候,一般都是将方法写在原型上,有固定值的属性也要写在原型上;只有需要传递参数的配置项才写在构造函数上
    3. function Handelphone(color, brand, system){
    4. this.color = color;
    5. this.brand = brand;
    6. this.system = system;
    7. }
    8. Handelphone.prototype.screen = '18:9';
    9. Handelphone.prototype.rom = '64G';
    10. Handelphone.prototype.ram = '6G';
    11. Handelphone.prototype.call = function(){
    12. console.log('I am calling somebody');
    13. }
    14. var hp1 = new Handelphone('red', '小米', 'Android');
    15. var hp2 = new Handelphone('black', 'iPone', 'IOS');
    16. console.log(hp1.ram);
    17. console.log(hp2.rom);
    18. console.log(hp1.screen);
    19. console.log(hp2.screen);
    20. // 代码继续优化
    21. function Handelphone(color, brand, system){
    22. this.color = color;
    23. this.brand = brand;
    24. this.system = system;
    25. }
    26. Handelphone.prototype = {
    27. ram: '6G',
    28. rom: '64G',
    29. screen: '18:9',
    30. call: function(){
    31. console.log('I am calling somebody');
    32. }
    33. }
    34. var hp1 = new Handelphone('red', '小米', 'Android');
    35. var hp2 = new Handelphone('black', 'iPone', 'IOS');
    36. console.log(hp1.ram);
    37. console.log(hp2.rom);
    38. console.log(hp1.screen);
    39. console.log(hp2.screen);

    二、实例化对象无法增删改原型上的属性和方法

  • 实例化的对象是无法增删改原型上的方法,但是可以查询到原型上的方法

    1. // 实例化的对象是无法增删改原型上的方法,但是可以查询到原型上的方法
    2. function Test(){}
    3. Test.prototype.name = '我是prototype上的属性';
    4. var test = new Test();
    5. // 实例化对象可以访问原型上的属性或者是方法
    6. console.log(Test.prototype);
    7. console.log(test.name);
    8. // 实例化对象无法向原型上增加属性或者是方法
    9. test.num = 1;
    10. console.log(Test.prototype, test);
    11. // 实例化对象本身无法删除原型上的方法
    12. delete test.name;
    13. console.log(Test.prototype, test);
    14. // 实例化对象本身无法修改原型上的属性或者是方法
    15. test.name = 'proto';
    16. console.log(Test.prototype, test);

    三、函数原型上的constructor属性

  • 每一个函数都有一个prototype属性,在prototype上有一个constructor属性,它指向构造器,也就是构造函数本身

  • 构造函数原型上的constructor属性的指向是可以更改的

    1. // 每一个函数都有一个原型属性「prototype」
    2. // 函数的原型上「prototype」有一个constructor属性,它指向的是构造器,也就是构造函数本身
    3. // 函数原型上的constructor属性的指向是可以更改的
    4. function Telephone(){}
    5. function Handphone(color, brand, system){
    6. this.color = color;
    7. this.brand = brand;
    8. this.system = system;
    9. }
    10. Handphone.prototype = {
    11. constructor: Telephone,
    12. screen: '5.7'
    13. }
    14. console.log(Handphone.prototype);
    15. console.log(Handphone.prototype.constructor);

    四、实例化对象上的proto属性

  • 只有实例化的对象才会有proto属性,不实例化的对象是没有该属性的

  • proto属于每一个实例化对象,而不属于构造函数本身
  • proto其实就是每个实例化对象存储原型prototype的容器而已

    1. // 只有实例化的对象才有__proto__属性,没有实例化的对象是没有该属性的
    2. // __proto__属于的是每一个实例化对象本身,而不属于构造函数本身
    3. // __proto__属性其实就是每一个实例化对象用来存储原型prototype属性的一个容器
    4. function Car(){
    5. // 隐式
    6. var this = {
    7. __proto__: Car.prototype;
    8. }
    9. // this.name = 'Mazda'
    10. }
    11. Car.prototype.name = 'Benz';
    12. var car = new Car();
    13. console.log(car.name);

    五、原型上的proto属性的指向可以更改

  • 原型prototype上的proto属性也是可以更改其指向的

    1. // 原型上的__proto__属性也是可以更改其指向的
    2. function Person(){}
    3. Person.prototype.name = '张三';
    4. var p1 = {
    5. name: '李四'
    6. }
    7. console.log(Person.prototype.constructor);
    8. Person.prototype.constructor = p1;
    9. console.log(Person.prototype.constructor);

    六、实例化之前和之后

    ```javascript

    1. function Car(){}
    2. Car.prototype.name = 'Mazda';
    3. var car = new Car();
    4. Car.prototype.name = 'Benz';
    5. console.log(car.name);
  1. function Car(){}
  2. Car.prototype.name = 'Mazda';
  3. var car = new Car();
  4. console.log(car.name);
  5. Car.prototype.name = 'Benz';
  1. - 每一个函数都有一个原型prototype属性,原型上有一个constructor属性,它指向的是构造器,也就是当前这个构造函数本身
  2. - 在实例化对象之后再去更改原型上的属性和方法,并不会对实例化之前的对象产生影响
  3. ```javascript
  4. Car.prototype.name = 'Benz';
  5. function Car(){}
  6. var car = new Car();
  7. // 在实例化对象之后再去更改原型上的属性和方法,并不会对实例化之前的对象产生影响
  8. Car.prototype = {
  9. name: 'Mazda'
  10. }
  11. console.log(car.name);

七、window和return的问题

  1. function func(){
  2. window.b = 10;
  3. }
  4. func();
  5. console.log(b);
  6. // window和return的问题
  7. function test(){
  8. var a = 1;
  9. function add1(){
  10. a++;
  11. console.log(a);
  12. }
  13. return add1;
  14. }
  15. var add = test();
  16. add();
  17. add();
  18. add();
  19. // 通过window的方式来实现上面的test函数
  20. function compute(){
  21. var a = 1;
  22. function add(){
  23. a++;
  24. console.log(a);
  25. }
  26. window.add = add;
  27. }
  28. compute();
  29. add();
  30. add();
  31. add();
  32. // 通过自执行函数的方式来实现compute函数
  33. var add = (function(){
  34. var a = 1;
  35. function add(){
  36. a++;
  37. console.log(a);
  38. }
  39. return add;
  40. })();
  41. add();
  42. add();
  43. add();
  44. ;(function(){
  45. var a = 1;
  46. function add(){
  47. a++;
  48. console.log(a);
  49. }
  50. window.add = add;
  51. })()
  52. add();
  53. add();
  54. add();

八、插件化

  • 用自执行函数来写插件,是为了防止全局变量污染
  • 里面写构造函数是为了方便构造出新的实例化对象
  • 最后用window.xxx是为了将该构造函数抛到全局去,方便使用

    1. // 插件化
    2. // 用自执行函数是为了防止全局变量污染
    3. // 用构造函数是为了方便构造实例化对象
    4. ;(function(){
    5. function Test(){
    6. }
    7. Test.prototype = {
    8. }
    9. window.Test = Test;
    10. })()
    11. var test = new Test();

    九、作业

  • 写一个插件,调用该方法即可实现加减乘除

    1. // 作业
    2. // 写一个插件,调用该方法即可实现加减乘除
    3. ;(function(){
    4. var Compute = function(opt){
    5. this.x = opt.firstNum;
    6. this.y = opt.secondNum;
    7. }
    8. Compute.prototype = {
    9. add: function(){
    10. return this.x + this.y;
    11. },
    12. minus: function(){
    13. return this.x - this.y;
    14. },
    15. mul: function(){
    16. return this.x * this.y;
    17. },
    18. div: function(){
    19. return this.x / this.y;
    20. }
    21. }
    22. window.Compute = Compute;
    23. })();
    24. var compute = new Compute({
    25. firstNum: 8,
    26. secondNum: 2
    27. });
    28. console.log(compute.add());
    29. console.log(compute.minus());
    30. console.log(compute.mul());
    31. console.log(compute.div());
    1. // 写一个插件,调用该方法即可实现加减乘除
    2. ;(function(){
    3. var compute = function(){};
    4. function Compute(){}
    5. Compute.prototype = {
    6. add: function(a, b){
    7. return a + b;
    8. },
    9. minus: function(a, b){
    10. return a - b;
    11. },
    12. mul: function(a, b){
    13. return a * b;
    14. },
    15. div: function(a, b){
    16. return a / b;
    17. }
    18. }
    19. window.Compute = Compute;
    20. })();
    21. var compute = new Compute();
    22. console.log(compute.add(10, 5));
    23. console.log(compute.minus(10, 5));
    24. console.log(compute.mul(10, 5));
    25. console.log(compute.div(10, 5));