面向对象

  1. 对象:大自然所有的事物都是可以被称为对象的
  2. 类:把抽象的‘对象’,按照特点进行详细的分类(大类/小类),把共同的东西进行抽取,放到对应的类别里
  3. =>'类'是对象的细分,和公共部分的封装,类是函数数据类型
  4. 实例:类别中派出来的具体事物叫做类的'实例'
  5. =>实例既有属于自己私有的东西,也有继承各个类别中的共有信息,对象数据类型的
  6. 面向对象:掌握对象,实例,类之间的关系和知识。实例都是对象数据类型的
  7. 研究一个实例
  8. 1. 私有的信息
  9. 2. 所属类共有的信息
  10. 3. 一层层从小类象大类研究属性和方法(直到ObJECT
  11. js的类
  12. 内置类和自定义类
  13. 内置类:Number String Boolean Null Object Aray...

单例模式

把描述同一件事物的不同属性放进同一个命名空间下,避免全局变量的干扰和冲突,

  1. /*
  2. * 公共模块
  3. */
  4. let utils=(function(){
  5. let queryElement=function(){...}
  6. return {
  7. //queryElement:queryElement
  8. queryElement
  9. }
  10. })();
  11. /*
  12. * 负责的页卡模块
  13. */
  14. let pageTabModule=(function(){
  15. //=>获取元素(调取其它命名空间下的方法)
  16. let tabBox=utils.queryElement('.tabBox');
  17. let show=function(){...}
  18. ...
  19. return {
  20. init:function(){
  21. //调用自己模块下的方法
  22. show();
  23. }
  24. }
  25. })();
  26. pageTabModule.init();

工厂模式

把实现统一功能的代码放进函数体中,当实现类似的功能,直接执行当前这个函数;减少代码冗余,实现高内聚,低耦合->函数的封装

  1. function descPerson(name,age) {
  2. var obj = {};
  3. obj.name = name;
  4. obj.age = age;
  5. obj.hobby = function () {
  6. console.log("I like JS")
  7. }
  8. return obj;
  9. };
  10. var a =descPerson("黄",9);
  11. var b =descPerson("张",13);
  12. console.log(a);
  13. console.log(b);

实例创建

1.字面量方式创建

  1. //var num=1;//字面量创建的是number类,但是属于基本数据类型
  2. 通过字面量方式创建的引用数据类型的值是一个标准的实例
  3. let obj={};
  4. //实例创建,如果只有一个参数实例创建;如果只有一个参数并且是数字,那么代表当前实例的长度
  5. // 如果两个及以上的,把当前当成数组的每一项返回;
  6. var num1 = new Number(12);// 标准的实例;
  1. 构造函数创建

    构造函数执行,因为也具备普通函数执行的特点
    1.和实例有关系的操作一定是 this.xxx=xxx ,因为this是当前类创造出来的实例
    2.私有变量和实例没有必然的联系

  1. var n=new Number(12);//创建的结果是number类的实例,是对象数据类型值,他的原始值是12
  2. // console.log(Number.prototype);//=>查看Number类为其实例赋予的属性和方法
  3. // console.log(num.toFixed(2));//=>虽然是基本类型值,但是也是Number的实例,依然可以调取类中的属性和方法(浏览器在执行的时候会默认的把num进行new Number(num)的处理)
  4. // console.log(n.toFixed(2));
  1. 引用数据类型创建的区别
  1. //=>对于引用数据类型两种创建方式的区别:仅仅是语法上的区别
  2. // var obj1 = {name: '珠峰培训'};//=>字面量创建方式
  3. // var obj2 = new Object();//=>构造函数创建方式
  4. // obj2['name'] = '珠峰培训';
  5. // var ary1 = [12];//=>创建一个数组存储12
  6. // var ary2 = new Array(12);//=>创建一个数组,长度是12位,每一项都是空
  7. // function fn() {
  8. // console.log('ok');
  9. // }
  10. // var fn = new Function("console.log('ok')");

构造函数

在普通函数前加一个new,当前函数就变成一个构造函数,构造函数执行会默认创建一个实例,类一定是一个函数,但函数不一定是类。

  1. /*
  2. NEW执行也会把类当做普通函数执行(当然也有类执行的一面)
  3. * 1、new Fn也是先把Fn执行,形成一个新的私有作用域
  4. * 2、形参赋值
  5. * 3、变量提升 (构造函数执行也具备普通函数的一面)
  6. * 4、代码执行前:浏览器会在当前栈内存中创建一个对象数据类型值,而且让作用域中的THIS指向创建的这个对象,而这个对象就是当前类的一个实例(构造函数执行形成的栈内存中THIS是当前类的实例)
  7. * 5、代码自上而下执行:如果遇到了this.xxx=xxx相当于给当前类的实例增加一些私有的属性
  8. * 6、即时我们不写return,浏览器也会默认把创建的实例返回(如果我们自己写了return:返回的是基本类型值对返回的实例不影响,如果返回的是引用类型值,会把默认返回的实例替换掉)
  9. */
  10. function Fn(){
  11. this.x = arguments[0];
  12. this.y = arguments[1];
  13. }
  14. //Fn();// undefined
  15. //Fn;
  16. var f = new Fn(10, 20);// 1.让函数执行; 并且让当前函数变成一个构造函数(类);
  17. //
  18. console.log(f);// {}

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

  1. 1. 函数执行:形成私有作用域--> 形参赋值---> 变量提升--> (会默认创建一个空对象,
  2. 并且把函数中的this指向当前这个对象)代码从上到下运行(会把这个对象返回)--->
  3. 作用域销毁;
  4. 2. 函数执行时,私有变量和实例没有直接的关系;和函数体中的this有关;
  5. 3. 构造函数如果没有实参,那么构造函数小括号可以省去;
  6. 4. 构造函数中如果return出基本数据类型值,那么不会影响构造函数返回的实例;
  7. 如果是return出一个引用数据类型值;那么会把默认return的实例覆盖;

原型&原型链

原型

  1. 1. 每一个函数数据类型的值,都有一个天生自带的属性:prototype(原型),这个属性的属性值是一个对象(“用来存储实例公用属性和方法”)
  2. - 普通的函数
  3. - 类(自定义类和内置类)
  4. 2. prototype这个对象中,有一个天生自带的属性:constructor,这个属性存储的是当前函数本身
  5. ```javascript
  6. Fn.prototype.constructor === Fn
  1. 每一个对象数据类型的值,也有一个天生自带的属性:_proto_,这个属性指向“所属类的原型prototype”

    • 普通对象、数组、正则、Math、日期、类数组等等
    • 实例也是对象数据类型的值
    • 函数的原型prototype属性的值也是对象类型的
    • 函数也是对象数据类型的值 ```
  1. function Fn(name,age){
  2. this.age=age;
  3. this.name=name;
  4. this.say=function(){
  5. console.log('my name is'+this.name);
  6. }
  7. }
  8. Fn.prototype.say=function(){
  9. console.log('i am ' + this.age + ' years old');
  10. }
  11. Fn.prototype.eat = function () {
  12. console.log('i love food');
  13. };
  14. var f1 = new Fn('张学波', 20);
  15. var f2 = new Fn('冀闯', 21);
  16. f1.say();//=>this:f1 查找私有的
  17. f1.__proto__.say();//=>查找的是公有的属性和方法 this:f1.__proto__ 'i am ' + f1.__proto__.age (undefined) + ' years old'
  18. Fn.prototype.say();
  1. 原型链中的this指向
  1. 原型链中提供的私有(公有)方法中的this指向
  2. 1. 点前面是谁this就是谁
  3. 2. 把需要执行方法中的this进行替换
  4. 3. 替换完成后,如果想要知道结果,只需要按照原型链的查找机制去查找即可

原型链查找机制

  1. 1.先找自己私有的属性,有则调取使用,没有继续找
  2. 2.基于\__proto\__找所属类原型上的方法(Fn.prototype),如果还没有则继续基于\__proto\__往上找...
  3. 一直找到Object.prototype为止
  4. function Fn() {
  5. this.x = 100;
  6. this.y = 200;
  7. this.getX = function () {
  8. console.log(this.x);
  9. }
  10. }
  11. Fn.prototype.getX = function () {
  12. console.log(this.x);
  13. };
  14. Fn.prototype.getY = function () {
  15. console.log(this.y);
  16. };
  17. var f1 = new Fn;
  18. var f2 = new Fn;
  19. console.log(f1.getX === f2.getX);//=>false
  20. console.log(f1.getY === f2.getY);//=>true
  21. console.log(f1.__proto__.getY === Fn.prototype.getY);//=>true
  22. console.log(f1.__proto__.getX === f2.getX);//=>false
  23. console.log(f1.getX === Fn.prototype.getX);//=>false
  24. console.log(f1.constructor);//=>Fn
  25. console.log(Fn.prototype.__proto__.constructor);//=>Object
  26. f1.getX();//=>this:f1 =>console.log(f1.x); =>100
  27. f1.__proto__.getX();//=>this:f1.__proto__ =>console.log(f1.__proto__.x); =>undefined
  28. f2.getY();//=>this:f2 =>console.log(f2.y); =>200
  29. Fn.prototype.getY();//=>this:Fn.prototype =>console.log(Fn.prototype.y); =>undefined

重定向问题

  1. /*
  2. * 重构类的原型:让某个类的原型指向新的堆内存地址(重定向指向)
  3. * 问题:重定向后的空间中不一定有CONSTRUCTOR属性(只有浏览器默认给PROTOTYPE开辟的堆内存中才存在
  4. CONSTRUCTOR),这样导致类和原型机制不完整;所以需要我们手动再给新的原型空间设置CONSTRUCT
  5. OR属性;
  6. * 问题:在重新指向之前,我们需要确保原有原型的堆内存中没有设置属性和方法,因为重定向后,原有的属性
  7. 和方法就没了(如果需要克隆到新的原型堆内存中,我们还需要额外的处理) =>但是内置类的原
  8. 型,由于担心这样的改变会让内置的方法都消失,所以禁止了我们给内置类原型的空间重定向,例如:
  9. Array.prototype={...}这样没有用,如果想加方法Array.prototype.xxx=function(){...}可
  10. 以这样处理
  11. */
  12. //内置类原型不能进行重构
  13. //=>当我们需要给类的原型批量设置属性和方法的时候,一般都是让原型重定向到自己创建的对象中
  14. function Fn() {
  15. // ...
  16. }
  17. Fn.prototype.xxx = function () {}
  18. //=>批量给原型设置属性方法的时候:重构类的原型
  19. Fn.prototype = {
  20. constructor: Fn,
  21. getA: function () {},
  22. getB: function () {}
  23. }; */
  24. /* //=>批量给原型设置属性方法的时候:设置别名
  25. let proto = Fn.prototype;
  26. proto.getA = function () {}
  27. proto.getB = function () {}
  28. proto.getC = function () {}
  29. proto.getD = function () {} */

基于内置类的原型扩展方法

  1. 在内置类原型上的方法,类所对应的实例可以直接调取使用,例如:实例.方法() ary.push()
  2. 如果我们也把自己写的方法放到原型上,那么当前类的实例也可以直接这样调取使用了
  3. 注意的地方

    1.自己扩展的方法不能影响原有内置的方法(我们自己设置的方法最好加前缀:my)
    2.扩展方法中的THIS一般都是当前类的实例(也就是要操作的值):实例.方法()

    1. ~ function () {
    2. /*
    3. * myUnique : 实现数组去重
    4. * @params
    5. * @return
    6. * [Array] 去重后的数组
    7. * by zhouxiaotian on 20190805
    8. */
    9. function myUnique() {
    10. //此时没有传递要操作的ARY进来,但是方法中的THIS是当前要操作的数组:ARY.MYUNIQUE()
    11. let obj = {};
    12. for (let i = 0; i < this.length; i++) {
    13. let item = this[i];
    14. if (typeof obj[item] !== 'undefined') {
    15. this[i] = this[this.length - 1];
    16. this.length--;
    17. i--;
    18. continue;
    19. }
    20. obj[item] = item;
    21. }
    22. obj = null;
    23. // 保证当前方法执行完返回的结果依然是ARRAY类的一个实例
    24. return this;
    25. }
    26. //=>扩展到内置类的原型上
    27. Array.prototype.myUnique = myUnique;
    28. }();
    29. let ary = [12, 23, 13, 12, 23, 24, 34, 13, 23];
    30. ary.myUnique();

    链式写法

    执行完成一个方法紧跟着就会调去下一个方法(执行完成一个方法后,返回的结果依然是当前类的实例,就可以继续调取当前类的其他方法操作)

任务:hasPubProperty,slice,去重