原生JavaScript对象的理解

JS原生对象

我们可以通过一个贪吃蛇小案例来分析js的对象使用和对比js过程开发

贪吃蛇小案例:https://github.com/GitHubKing89/JavaScript-Object/blob/master/JS贪吃蛇

JavaScript中没有类的概念,只有对象。

在JavaScript中定义对象可以采用以下几种方式:

1.基于已有对象扩充其属性和方法

2.工厂方式

3.构造函数方式

4.原型(“prototype”)方式

5.动态原型方式

方式一:

<script type="text/javascript">
var object = new Object();
//新增属性
object.name = "zhangsan";
//新增方法
object.sayName = function(name)
{
       this.name = name;
       alert(this.name);
}
//方法调用
object.sayName("lisi");
</script>

这种方式的弊端:这种对象的可复用性不强,如果需要使用多个对象,还需要重新扩展其属性和方法。

方式二:

//工厂方法这里可以看做是函数封装
    function createObject()
    {
           var object = new Object();
           object.username = "zhangsan";
           object.password = "123";
           object.get = function()
           {
                  alert(this.username + ", " + this.password);
           }
           return object;
    }
    //创建对象
    var object1 = createObject();
    var object2 = createObject();
    object1.get();//调用方法

方式二改进1;

function createObject(username, password)//传入参数
{
       var object = new Object();
       object.username = username;
       object.password = password;
       object.get = function()
       {
              alert(this.username + ", " + this.password);
       }
           return object;
}
    var object1 = createObject("zhangsan", "123");
object1.get();

方式二改进2;

//共用函数
    function get()
    {
           alert(this.username + ", " + this.password);
    }
      //函数对象只有一份
    function createObject(username, password)
    {
           var object = new Object();
           object.username = username;
           object.password = password;
           object.get = get; //每一个对象的函数对象都指向同一个函数对象
           return object;
    }
    var object = createObject("zhangsan", "123");
    var object2 = createObject("lisi", "456");
    object.get();
    object2.get();

优点:让一个函数对象被多个对象所共享,而不是每一个对象拥有一个函数对象。

缺点:对象和它的方法定义分开了,可能会造成误解和误用。

方式三

`**

构造函数的定义方法其实和普通的自定义函数相同。

**`

function Person()
{
       //在执行第一行代码前,js引擎会为我们生成一个对象
       this.username = "zhangsan";
       this.password = "123";
       this.getInfo = function()
       {
              alert(this.username + ", " + this.password);
       } 
       //此处有一个隐藏的return语句,用于将之前生成的对象返回
       //只有在后面用new的情况下,才会出现注释所述的这两点情况
}
//生成对象
var person = new Person();//用了new
person.getInfo();

带参改进

function Person(username, password)
{
       this.username = username;
       this.password = password;

       this.getInfo = function()
       {
              alert(this.username + ", " + this.password);
       }
}
var person = new Person("zhangsan", "123");
person.getInfo();

方式四(对象原型prototype

function Person(){
}
Person.prototype.username = "zhangsan";
Person.prototype.password = "123";
Person.prototype.getInfo = function() {
       alert(this.username + ", " + this.password);
}
var person = new Person();
var person2 = new Person();
person.username = "lisi";
person.getInfo();
person2.getInfo();

如果使用原型方式来定义对象,那么生成的所有对象会共享原型中的属性,这样一个对象改变了该属性也会反映到其他对象当中。
单纯使用原型方式定义对象无法在构造函数中为属性赋初值,只能在对象生成后再去改变属性值。

对象原型改进

使用原型+构造函数方式来定义对象,对象之间的属性互不干扰,各个对象间共享同一个方法。

<script type="text/javascript">
//使用原型+构造函数方式来定义对象
function Person() {
       this.username = new Array();
       this.password = "123";
}
Person.prototype.getInfo = function(){
       alert(this.username + ", " + this.password);
}
var p = new Person();
var p2 = new Person();
p.username.push("zhangsan");
p2.username.push("lisi");
p.getInfo();
p2.getInfo();
</script>

方式五(只是改进了prototype方法的重复问题)

在构造函数中通过标志量让所有对象共享一个方法,而每个对象拥有自己的属性。
<script type="text/javascript">
    function Person() {
       this.username = "zhangsan";
       this.password = "123";
       if(typeof Person.flag == "undefined") {
              //此块代码应该只在第一次调用的时候执行
              alert("invoked");
               Person.prototype.getInfo = function() {
                     //这个方法定义在原型中,会被每一个对象所共同拥有
                     alert(this.username + ", " + this.password);
              }
              Person.flag = true;//第一次定义完之后,之后的对象就不需要再进来这块代码了
       }
}
var p = new Person();
var p2 = new Person();
p.getInfo();
p2.getInfo();
</script>

js对象的拷贝

浅拷贝

**浅拷贝分两种情况,一是直接拷贝源对象的引用,二是源对象拷贝实例,但其属性(类型为Object、Array的属性)拷贝引用。**

对于对象实例的拷贝,常用的方法有:Array.prototype.slice(), Array.prototype.concat(), jQuery的$.extend({},obj)

例1:

    var a = [{c:1}, {d:2}];
    var b = a.slice();
    console.log(a === b); // 输出false,说明外层数组拷贝的是实例
    a[0].c = 3;
    console.log(b[0].c); // 输出 3,说明其元素拷贝的是引用

例2:

  <script>
         // 对象的拷贝
         var obj1 = {
           name: 'zs',
           age: 18,
           sex: '男',
           dog: {
             name: '金毛',
             age: 2,
             yellow: '黄色'
           }
         }
         var obj2 = {};
        // 封装函数  - 把o1 的成员,复制给o2
        // 浅拷贝
        function copy(o1, o2) {
          for (var key in o1) {
            o2[key] = o1[key];
          }
        }
        copy(obj1, obj2);
        // 修改obj1中的成员
        obj1.name = 'xxxx';
        obj1.dog.name = '大黄';
        console.dir(obj2);//输出结果:Object    age :18  (dog: Object ----> age: 2  name: "大黄"  yellow: "黄色"       __proto__: Object )
        name  : "zs"     sex  :  "男"
        </script>
        案例想说的是浅拷贝只能对简单属性copy,嵌套对象只是对象引用

function extend(parent, child) {
for (var key in parent) {
// 不给wsc复制同名的属性
if (child[key]) {
continue;
}
child[key] = parent[key];
}
}

深拷贝

深拷贝后,两个对象,包括其内部的元素互不干扰。常见的方法有JSON.parse(),JSON.stringify(),jquery的$.extend(true,{}, obj),lodash的_.cloneDeep和_.clone(value, true)。

例1:

var a = {c: {d: 1}};
var b = $.extend(true, {}, a);
console.log(a === b); // 输出false
a.c.d = 3;
console.log(b.c.d); // 输出 1,没有改变。

例2:

<script>
    // 深拷贝
    var obj1 = {
      name: 'zs',
      age: 18,
      sex: '男',
      dog: {
        name: '金毛',
        age: 2
      },
      friends: ['ls', 'ww']
    }
    // 深拷贝  把o1的成员拷贝给o2
    function deepCopy(o1, o2) {
      for (var key in o1) {
        // 获取key属性对应的值
        var item = o1[key];
        // 如果item 是对象?
        // var o = {}
        if (item instanceof Object) {
          // var o = {};
          o2[key] = {}; 
          deepCopy(item, o2[key]);
        } else if (item instanceof Array) {
          // 如果item 是数组呢?
          // var arr = [];
          o2[key] = [];
          //使用了递归
          deepCopy(item, o2[key]);
        } else {
          // 如果是简单类型
          o2[key] = o1[key];
        }
      }
    }
     var obj2 = {};
     deepCopy(obj1, obj2);
    // 修改obj1中的成员 是否会影响obj2?
    obj1.dog.name = 'xxx';
    obj1.friends[0] = 'xxx';
     console.dir(obj2);
  </script>

演示JS的原型继承

例:

 <script>
    // 继承:类型和类型之间的关系
    // 学生类型  老师类型  -> Person类型
    // 继承目的: 把子类型中共同的成员提取到父类型中,代码重用

    // 父类型
    function Person() {
      this.name = 'zs';
      this.age = 18;
      this.sex = '男';
    }
    // 子类型
    function Student() {
      this.score = 100;
    }
    Student.prototype = new Person();
    Student.prototype.constructor = Student;//必须指定闭包为子类型
    var s1 = new Student();
    console.log(s1.constructor);
    console.dir(s1);
       function Teacher() {
      this.salary = 3000;
    }
    // 原型继承: 无法设置构造函数的参数
  </script>

例2:

**

利用 call改变this对象的指向来实现继承构造函数的传参

**

<script>
    // 借用构造函数
    // 父类型
    function Person(name, age, sex) {
      this.name = name;
      this.age = age;
      this.sex = sex;
      // this.sayHi
    }
    Person.prototype.sayHi = function () {
      console.log(this.name);
    }
    // 子类型
    function Student(name, age, sex, score) {
      Person.call(this, name, age, sex);
      this.score = score;
    }
    var s1 = new Student('zs', 18, '男', 100);
    console.dir(s1);
  </script>

组合继承

      <script>
        // 组合继承:借用构造函数 + 原型继承
    
        // 父类型
        function Person(name, age, sex) {
          this.name = name;
          this.age = age;
          this.sex = sex;
        }
          Person.prototype.sayHi = function () {
          console.log('大家好,我是' + this.name);
        }
            // 子类型
        function Student(name, age, sex, score) {
          // 借用构造函数
          Person.call(this, name, age, sex);
          this.score = score;
        }
    
        // 通过原型,让子类型,继承父类型中的方法
        Student.prototype = new Person();
        Student.prototype.constructor = Student;
        // 学生特有的方法
        Student.prototype.exam = function () {
          console.log('考试');
        }
        // var s1 = new Student('zs', 18, '男', 100);
        // console.dir(s1);
        // var p1 = new Person('ls', 18, '男');
        // console.dir(p1);
        function Teacher(name, age, sex, salary) {
          // 借用构造函数
          Person.call(this, name, age, sex);
          this.salary = salary;
        }
            // 通过原型让子类型继承父类型中的方法
        Teacher.prototype = new Person();
        Teacher.prototype.constructor = Teacher;
        var t1 = new Teacher('ww', 30, '男', 100000);
        console.dir(t1);
        t1.sayHi();
 </script>

面向对象的特性:封装、继承、多态

javascript 对照java的面向对象只能说是为了面向对象的思想而强有。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值