面向对象编程

这是一篇简单介绍js面向对象的编程笔记

一,js解析和执行

  js解析时,会先把var和function声明的变量和函数放到一个词法对象里,变量的值是undefined,函数则是一个引用,这是js变量提升的根本机制。

  如果一个变量没有声明就开始使用了,不论它是在局部还是全局使用,它都是挂在了window下,是个全局变量,这点和this指向很类似,this找不到直接调用者,也是直接指向window。

  如果变量名和函数名命名冲突了,则该命名最后指向的是函数。如果两个函数名重复了,则后面一个函数覆盖前面的,因为在js中,函数才是第一等公民。

    e.g. 
    console.log(a)//undefined
    var a = 1
    console.log(a)//1

    e.g.
    console.log(aa)//function aa() {console.log('bbbbb')}
    var aa = 1
    function aa() {
       console.log('aaaaa')
    }
    function aa() {
       console.log('bbbbb')
    }

二,作用域

  js只有全局作用域和局部(函数)作用域,并且内部可以由内到外,从近到远,访问即止地层层访问外部变量,形成一条作用域链,而外部不能直接访问内部变量。

    e.g.
    var a = 1
    var b = function() {
        var c = 2+a
        console.log(c)
    }
    b()//3
    console.log(c)//undefined

三,闭包

  一种访问局部变量的手段,通常指的是一个可以访问局部变量的函数。

  闭包一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中,因此要注意控制性能,提高网页性能。

    e.g.
  var name = "The Window";
  var object = {
    name : "My Object",
        // 这个方法是返回了一个函数
    getNameFunc : function(){
      return function(){
        return this.name;
      };
    }
  };
    // 执行了object.getNameFunc()返回的函数,此时this找不到直接调用者,直接指向window
  alert(object.getNameFunc()());

    e.g.
  var name = "The Window";
  var object = {
    name : "My Object",
        // 这个方法是返回了一个函数
    getNameFunc : function(){
            // that指向的是object
      var that = this;
      return function(){
        return that.name;
      };
    }
  };
    // 执行了object.getNameFunc()返回的函数,此时this固定化成了object,弹出object.name
  alert(object.getNameFunc()());

四,类与对象

  搞清楚js中的类,就需要明白三个概念:构造函数、原型对象,实例化和实例。

    e.g.
    function Person(name,age) {
        this.name = name;
        this.age = age
    }
    Person.prototype.hello = function () {
        console.log('Hello,I am '+this.name+',and I am '+this.age)
    }
    var xm = new Person('xiaoming',12)
    xm.hello()//Hello,I am xiaoming,and I am 12
    Person.prototype = {
        asset:12345
    }
    var xmm = new Person('xiaomingming',34)
    console.log(xm.asset)//undefined
    console.log(xmm.asset)//12345

其中Person函数就是构造函数,Person.prototype指向的对象就是原型对象,xm就是实例

 

  那么在关键词new的过程中,大概发生了什么呢?

  1,一个新对象被创建了,继承Person.prototype原型对象里的属性。
  2,使用指定的参数调用构造函数Person,并将this指向新创建的对象。

  如果我们用自定义函数模拟过程,则:

    e.g.
    function Person(name,age) {
        this.name = name;
        this.age = age
    }
    function New(f) {
        return function() {
            var o = {
                '__proto__':f.prototype
            }
            f.apply(o,arguments)
            return o
        }
    }
    var xmmm = New(Person)('xmmm',232)

  在js中,并没有真正的类的概念,只是用构造函数和原型对象模拟了类,其中原型对象是类的唯一标识,当且仅当两个对象继承自同一个原型对象的时候,我们才说它们是同一个类的实例。而初始化对象的构造函数则不能作为类的标识,通常构造函数的名字被用来当做类名了而已。

  instanceof 运算符
  实例 instanceof 类名 被用来判断一个实例是否继承自某个原型对象,返回布尔值。

  在上述例子中,原型对象也是对象,也具有原型对象,即原型对象的原型对象,以此类推。访问一个实例本身没有的属性或方法时,js会从原型对象上去找,原型对象上没有时,会从原型对象上的原型对象上去找,......这种像链式一样地继承机制就是原型链,和作用域链很类似。

  原型对象中constructor属性则是指向该原型对象的构造函数,在修改原型对象时,比如增删其属性时,可采用.符号操作,如Person.prototype.hello = s =>{console.log(s)},在实例person初始化的时候,该原型对象的constructor默认指向构造函数.

  但是如果直接给Person.prototype赋值对象型数据时,则需要显式指定constructor属性,否则原有的原型对象会被新对象所替代,而通过构造函数实例化的实例的__protoo__属性都会指向最新的Person.prototype对象。在这个改动之后实例化的实例均不能继承原有的原型对象的属性,改动之前的实例亦不能继承新原型对象的属性。

  实例,构造函数,原型对象,原型链之间的关系可以借助一张神图来说明关系

  this指针问题:

  1,总是指向方法的直接调用对象,在事件触发中指向触发该事件的dom节点对象

  2,指向new出来的实例

  apply和call均可以改变this指针,其区别仅仅是第二个参数的不同而已:

  f.call(o,a,b) === o.f(a,b) === f.apply(o,[a,b])

五,封装

  一段代码能够实现某种功能,隐藏该代码的细节,仅对外开放接口去使用该功能代码,即是对该功能代码的封装。

六,继承

  继承可以使得子类具有父类的属性和方法或者重新定义、追加属性和方法等。具有的方式可能是复制,可能是引用。

  从对象层面上来看继承:

  1,复制

        var zsh = {
            name :'zsh',
            age:23
        }
        var job = {
            job:'programer',
            language:'fe'
        }
        for (var key in job){
            zsh[key] = job[key]
        }
        console.log(zsh)//{name: "zsh", age: 23, job: "programer", language: "fe"}    

涉及到深浅复制参考:http://www.cnblogs.com/zhouxiaohouer/p/8037729.html

  2,Object.create(proto[, propertiesObject])

  es5里的方法,注意兼容性。

  参数
  proto 新创建对象的原型对象。
  propertiesObject 可选。如果没有指定为 undefined,则是要添加到新创建对象的可枚举属性(即其自身定义的属性,而不是其原型链上的枚举属性)对象的属性描述符以及相应的属性名称。这些属性对应Object.defineProperties()的第二个参数。
  返回值
  在指定原型对象上添加新属性后的对象。

 

  从类的层面上来看继承:

        // 创建父类
        // 创建子类
        // 建立联系,修正构造函数
        // 实例化
        function Father() {}
        function Son() {}
        Son.prototype = Object.create(Father.prototype)
        // 此时,Son.prototype.constructor指向的是function Father() {},我们最好是将其修正过来
        Son.prototype.constructor = Son

七,多态

  '见人说人话,见鬼说鬼话。'

  根据不同的参数个数,调用不同的方法,实现不同的功能

  本质上是检测arguments中数据类型来做对应的操作,将差异化的处理方式通用封装处理。

    e.g.
    function duotai(a,b,c,d,e,f) {
        console.log(duotai.length)//返回形参个数
        console.log(arguments.length)//返回实参个数
    }
    duotai(1,2,3,4)//6 4

    e.g.
    function add() {
        var result = 0
        for (var i = 0 ;i<arguments.length-1;i++){
            result += arguments[i]
        }
        return result
    }
    add(1,2,3,4,6,7,8)//31
    add(343,567,8456,234)//9600

 

posted @ 2017-12-19 21:02  周小猴儿  阅读(250)  评论(0编辑  收藏  举报