zoukankan      html  css  js  c++  java
  • JavaScript入门-对象(二)

    JavaScript对象(二)

    本篇,主要讲了面向对象、this的指向问题,模拟继承过程

    面向对象编程

    • 什么面向对象编程?
      • 编程,编程就是人们用计算机能懂的语言,告诉计算机自己想做的事情。
      • 面向对象的编程的主要思想是把构成问题的各个事物分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描述一个事物在解决问题的过程中经历的步骤和行为。对象作为程序的基本单位,将程序和数据封装其中,以提高程序的重用性,灵活性和可扩展性
        ps:在学习其它的语言,比如c语言,是面向过程的编程思想,我们现在了解一下,面向对象重点关注的是谁能帮我解决问题,面向过程重点关注的是解决问题的过程

    面向对象的三大特点

    • 封装。
      • 把资源整合
    • 继承
      • 使用继承可以用来复用代码。子类拥有了父类方法,以及重写
    • 多态
      • 一种事物的多种形态。分运行时多态(比如方法重写),编译时多态(比如方法重载)

    创建对象

    创建对象,主要可以分为两类;

    1. 工厂创建
    2. 构造函数

    工厂创建

        //举一个最简单的工厂创建例子
    
        //需要一个对象,直接工厂创建,调用方法就行
        var p = createPerson()
    
        function createPerson( name , age ){
            var obj = {}
            obj.name = name
            obj.age = age
            return obj
        }
    
    

    ps:工厂创建,就是把创建对象权力交给了他人,自己就只需要把参数告诉工厂就可以了。

    构造函数

        //构造函数
    
        function Person( name , age ){
            this.name = name
            this.age = age
            this.say = fuction(){
                ...
            }
        }
        //实例化一个对象
        var p2 = Person()
    

    注意:因为在JavaScript ES5里,就没有面向对象的写法,所以我们这上面的面向对象都是通过模拟来是实现的。在es6里,有class关键字,在类里写一个construct来构造。

    prototype

    我们有没有发现,在控制台打印一个function,把函数体展开就能看到一个prototype,这个prototype并不是我们自己定义的一个属性或者方法,那这个到底是什么呢?

    • prototype叫做原型。创建fn函数自动获得prototype属性,该属性是一个对象即该函数的原型对象,我们可以看到原型对象默认会有一个constructor属性,该属性是指向函数自身即fn。
    原型链

    我们把_proto_展开,你会在展开后的最下面还能看到一个_proto_,再展开下一个,发现又有,直到你到Object对象的_proto_。不过这得嵌套了多少层了。其实我们可以理解,这样的_proto_好比是继承关系。a继承了b,b就是a的父亲,那么a里的_proto_指向的就是b。

    应用:
    比如你要给数组写一个方法,叫做myPop(),删除最后一个元素。
    当你写完这个方法后,你可以通过原型方法,给一个数组对象Array叫上这个方法。Array.prototype.myPop = function(){...}
    那么,只要是Array类型的变量,就都可以直接调用myPop()方法了

    访问对象

    1. 用类似数组方式访问
        var m = new Man('rainbow' , 20 )
    
        访问name属性,console.log(m['name'])  //rainbow
    
    1. for。。。in遍历每个成员
        for ( const key in m ){
            console.log( m[key] )
        }
    

    删除对象

    var m = new Man('rainbow' , 20 )
    //删除指定对象 , 执行成员
    delete m1.name
    

    ps:删除完后,Man对象就没有name这个属性了。

    instance of

    之前有个typeof,是用来检查数据类型。

    • instance of是用来检查对象是否为某个对象的实例,返回true/false

    e.g.

    var m = new Man('rainbow' , 20 )
    m instance of Man   //true
    m instance of Woman   //false
    m instance of Object //true,object为任何对象的祖先
    

    hasOwnProperty

    • 检查对象是否有自己的属性.返回true/false
        var m = {
            name : 'rainbwo',
            age : 20
        }
        m.hasOwnProperty('name')    //true
        m.hasOwnProperty('sex')    //flase
    

    in

    • 检查对象中是否有该属性和方法。返回true/false
        var m = {
            name : 'rainbwo',
            age : 20
        }
        'name' in m     //true
        'age2' in m     //false
        'toString()' in m   //true
    

    ps: 对比hasOwnProperty、in

    • hasOwnProperty自定义的。in自定义的+继承的
    • hasOwnProperty只能检测当前对象中独有的属性和方法。in不仅仅是当前的,也能检测到所有继承而来的属性和方法。

    this关键字

    this关键字我们不陌生,之前在工厂创建对象时。用到了
    this.name = name
    this.age = age

    • 其实,这个this指的就是当前的对象,使用this可以不用把调用者本身全部拼写出来,并且易于阅读。

    • 但是,实际上,我们的this指向,并没有那么容易。

    比如:

    1. 在在全局中,console.log(this). 这个this打印的Window,也叫程序上下文。

    2. 在一个方法内部(箭头函数除外),console.log(this),这个this指的就是函数所属的对象

    3. 箭头函数的this指向,Window

    4. 在事件函数中,this指向事件本身。

    5. setTimeout,setInterval的this指向Window

    改变this指向的三种方法

    Function.prototype.xxx()

    • call()
    • apply()
    • bind()
        //定义test函数
        function test(attr1,attr2,attr3) {
            this.attr1 = attr1
            this.attr2 = attr2
            this.attr3 = attr3
        }
        //实例化一个对象
        var obj = new Object(a , b, c); 
    
    
    1. test.call( obj , 1 , 2 , 3 )

      call直接把this指向了obj

    2. test.apply( obj ,[1 , 2 , 3])

      apply指向了obj,并且用一个数组来传参数

    3. test.bind( obj ).(1 , 2 , 3)

      test.bind()把this指向了obj,并且返回了一个新的对象

    ps:在JavaScript的语法里,就只有这三种方法可以改变this的指向

    继承

    继承就是让子类拥有父类的属性和方法
    在es5里,没有继承的语法 ,es6有
    所以我们可以来模拟一下

        //父类
        function Person(name , age ) {
            this.name = name 
            this.age = age 
        }
    
        //子类
        Man 、 Woman
    
        function Man( name , age , job ){
            //构造函数的伪装,也就是通过this,改变指向
            Person.call(this , name , age )//等价于,调用了Persion()
            this.job = job
        }
        
    

    ps:

    1. 构造函数的伪装,其实就是继承了父类的属性和方法,用this指向的改变,推荐使用call()
    2. 但是,这样还不够,这里继承不完全。当我们使用instance of 检测,会找不到继承的属性以及方法。因此还需要最关键的一步
      Man.prototype = Person.prototype
      上面这一步叫做,原型继承。这样才算是真正的继承了。

    谢谢大家阅读,鄙人知识、能力不足,如果讲的不好或者不正确的地方,还请原谅。支持原创

  • 相关阅读:
    我的Git教程 之 初始化本地库
    再学Java 之 Integer 包装类缓存
    再学Java 之 private、protected、public和default的作用域
    再学Java 之 形参个数可变函数
    再学Java 之 foreach循环
    解决MySQL中文乱码问题
    再学Java 之 解决No enclosing instance of type * is accessible
    从一个例子学习 instanceof 和 getclass 的区别
    如何单独启动wamp 中自带的MySQL
    网络游戏程序员须知 UDP vs TCP(转)
  • 原文地址:https://www.cnblogs.com/lovelyk/p/14320416.html
Copyright © 2011-2022 走看看