zoukankan      html  css  js  c++  java
  • JS面向对象(3) -- Object类,静态属性,闭包,私有属性, call和apply的使用,继承的三种实现方法

    相关链接:

    JS面向对象(1) -- 简介,入门,系统常用类,自定义类,constructor,typeof,instanceof,对象在内存中的表现形式

    JS面向对象(2) -- this的使用,对象之间的赋值,for...in语句,delete使用,成员方法,json对象的使用,prototype的使用,原型继承与原型链

    JS面向对象(3) -- Object类,静态属性,闭包,私有属性, call和apply的使用,继承的三种实现方法

    1.Object类

    在JS中,Object是所有类的基类,使用Object类来创建自定义对象时,可以无需定义构造函数(constructor,prototype,hasOwnProperty(property))

    1 var per = new Object();
    2 per.name = 'zhangsan';
    3 per.age = 30;
    4 alert(per.name + per.age);

    我们想在程序中得到一个对象变量,只要能存储大量数据即可,这个时候,我们可以考虑使用Object类。Object类避免了对构造器的定义。 Object类下另一个常用的属性:hasOwnProperty

    1 var per = new Object();
    2 per.name = 'zhangsan';
    3 per.age = 30;
    4 if per.hasOwnProperty('email'){
    5     alert('具有email');
    6 }else{
    7     alert('无email');
    8 }

    2.静态属性

    在有些面向对象的语言当中,可以使用static关键字定义类的静态属性或者静态方法,在JS中,可以进行模拟。

    语法:

      类名.属性名

      类名.属性=function(){}

     1 function Person(){
     2 }
     3 Person.count = 0;
     4 var p1 = new Person();
     5 Person.count++;
     6 var p2 = new Person();
     7 Person.count++;
     8 var p3 = new Person();
     9 Person.count++;
    10 alert(Person.count);

    添加静态属性和静态方法:

     1 function Person(){
     2     Person.count++; //静态属性
     3     Person.getCount=function(){ //静态方法
     4         alert('当前共有' + Person.count + '个人');
     5     }
     6 }
     7 Person.count = 0;
     8 var p1 = new Person();
     9 var p2 = new Person();
    10 var p3 = new Person();
    11 Person.getCount();

    3.闭包

    概念:所谓闭包,指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因此这些变量也是该表达式的一部分。

    提出一个问题:

    1 function display(){
    2   var i=10;    
    3 }
    4 display();
    5 //在这里,想访问局部变量i

    在全局中,不能访问局部变量i,因为作用域不同,而且,在display函数执行完毕后,局部变量i会被回收。 闭包的功能:“访问局部变量”和“使变量所占的内存不被释放”

    1 //例1
    2 function fn1(){
    3     function fn2(){
    4         alert('hello');
    5     }
    6     return fn2; //返回fn2函数首地址
    7 }
    8 var test=fn1(); //test也指向了fn2函数的首地址
    9 test();

    通过例1我们知道:变量是可以指向函数的首地址的,函数也可以返回另一个函数的首地址。

     1 //例2
     2 function fn1(){
     3     var i = 10;
     4     function fn2(){
     5         alert(i);
     6     }
     7     return fn2; //返回fn2函数首地址
     8 }
     9 var test=fn1(); //test也指向了fn2函数的首地址
    10 test();

    通过例2我们知道:使用一个拒不函数包含变量i,这样局部变量i的内存不会被回收。

     1 //例3
     2 function fn1(){
     3     var i = 10;
     4     function fn2(){
     5         alert(i++);
     6     }
     7     return fn2; //返回fn2函数首地址
     8 }
     9 var test=fn1(); //test也指向了fn2函数的首地址
    10 test();
    11 test();
    12 test();

    在例3中,因为i的内存永远不会被回收,所以每次调用fn2,i的值会+1。运行的结果是弹出10,弹出11,弹出12。

    闭包的原理:在例3中,共有三个作用域:全局作用域,fn1的作用域,fn2的作用域。在全局作用域里有test=fn1(),其实这句话就相当于test=fn2。在fn1作用域里有 var i=10和return fn2,在fn2作用域例有alert(i++)。当全局作用域下的test=fn1()执行时,test指向了fn2的作用域,这个时候fn2作用域下的i被全局作用域钩住,根据作用域链的法则,fn2下并没有定义i,所以在fn2下的i往上一层作用域上找,找到了fn1作用域下的var i=10。所以全局的test钩住了fn2的i,fn2的i钩住了fn1的i,所以fn1运行完毕后,不会被回收。

    4.私有属性

    在面向对象思想中,对于有些敏感的,不想公开的成员可以定义为私有的,在JavaScript中可以模拟这个功能。

    语法: 

    function Person(p_name){

          var name = p_name;

      this.age

    }

    var :私有

    this :公有

    1 function Person(p_name,p_age){
    2     this.name = p_name;
    3     var age = p_age;
    4 }
    5 var p1 = new Person('zhangsan',30);
    6 alert(p1.name);
    7 alert(p1.age);

    在上面这个例子中,我们想用var来表示私有成员属性,但Person构造函数执行完毕后,age会被回收,不能当做成员属性来使用。

     1 function Person(p_name,p_age){
     2     this.name = p_name;
     3     var age = p_age;
     4     this.setAge=function(a){
     5         age = a;
     6     }
     7     this.getAge=function(){
     8         return(age);
     9     }
    10 }
    11 var p1 = new Person('zhangsan',30);
    12 p1.setAge(20);
    13 alert(p1.getAge());

    this.setAge和this.getAge两个方法使用到了局部变量age,所以age不会被回收。

    如果只有set方法,说明该属性是只写属性。

    如果只有get方法,说明该属性是只读属性。

    5.call和apply的使用

    call和apply的功能:使用指定的对象调用当前函数。call和apply的功能完全相同,只是在语法上略有不同。

    语法:

    call([thisObj[,arg1[,arg2[,argN]]]])

    第一个参数:函数执行时,this指向谁

    后面的参数:根据需要顺序指定

    apply([thisObj[,argArray]])

    第一个参数:函数执行时,this指向谁

    第二个参数:数组,表示参数集合

    在js中,函数有几种调用形式:

    Person();                      //Person内的this指向window

    var p1=new Person();    //Person内的this指向p1

    per.Person();                 //Person内的this指向per

     1 function Person(p_name,p_age){
     2     this.name = p_name;
     3     this.age = p_age;
     4 }
     5 function speak(){
     6     alert(this.name + this.age);
     7 }
     8 var p1 = new Person('zhangsan',30);
     9 //speak(); 这样调用this指向window
    10 //p1.speak(); p1对象没有speak属性

    使用call和apply来调用

     1 function Person(p_name,p_age){
     2     this.name = p_name;
     3     this.age = p_age;
     4 }
     5 function speak(){
     6     alert(this.name + this.age);
     7 }
     8 var p1 = new Person('zhangsan',30);
     9 speak.call(p1);
    10 speak.apply(p1);

    call和apply在执行时做了两件事:1)将函数内部this指向了第一个参数   2)调用函数

    另外:还可以这样解决问题:

    P1.say=speak;

    P1.say();

    这样解决和上面解决方法有本质上的区别:

    上面的解决办法是直接调用speak函数,只不过函数内部this的指向发生改变。

    下面的解决办法会为p1对象增加属性,p1对象的“体积”会变大。

    举例说明:

     1 <script>
     2 function fn1(){
     3     this.style.color='red';
     4 }
     5 function fn2(){
     6     this.style.fontSize='50px';
     7 }
     8 window.onload=function(){
     9     document.getElementById('btn').onclick=function(){
    10         var div1 = document.getElementById('div1');
    11         fn1.call(div1);
    12         fn2.apply(div1);
    13     };
    14 };
    15 </script>
    16 <div id='div1'>hello javascript</div>
    17 <input type='button' id='btn' value='确定'>

    6.继承的三种实现方法

    概念:在有些面向对象语言中,可以使用一个类(子类)继承另一个类(父类),子类可以拥有父类的属性和方法,这个功能可以在js中进行模拟。

    三种方法:

    第一种:扩展Object方法

    1 Object.prototype.方法=function(父类对象){
    2     for(var i in 父类对象){
    3         this[i] = 父类对象[i];
    4     } 
    5 };

    举例说明:

     1 Object.prototype.ext=function(parObject){
     2     //循环遍历父类对象所有属性
     3     for(var i in parObject){
     4         //为子类对象添加这个遍历到的属性
     5         //它的值是父类对象这个属性的属性值
     6         this[i] = parObject[i];
     7     }
     8 }
     9 function Person(p_name,p_age){
    10     this.name=p_name;
    11     this.age=p_age;
    12     this.speak=function(){
    13         alert(this.name+this.age);
    14     }
    15 }
    16 function Student(p_no){
    17     this.no=p_no;
    18     this.say=function(){
    19         alert(this.no+this.name_this.age);
    20     }
    21 }
    22 var stu = new Student(101);
    23 stu.ext(new Person('xiaoqiang',20));
    24 stu.speak();
    25 stu.say();

    第二种:使用call和apply方法

    语法:

    父类构造器.call(this,.......);

     1 function Person(p_name,p_age){
     2     this.name=p_name;
     3     this.age=p_age;
     4     this.speak=function(){
     5         alert(this.name+this.age);
     6     }
     7 }
     8 function Student(p_no,p_name,p_age){
     9     this.no=p_no;
    10     this.say=function(){
    11         alert(this.name+this.age+this.no);
    12     }
    13     Person.call(this,p_name,p_age);
    14 }
    15 var stu = new Student(8,'zhagsan',18);
    16 stu.speak();
    17 stu.say();

    第三种:原型继承

    语法:

    子类.prototype = new 父类();

     1 function Person(p_name,p_age){
     2     this.name=p_name;
     3     this.age=p_age;
     4     this.speak=function(){
     5         alert(this.name+this.age);
     6     }
     7 }
     8 function Student(p_no){
     9     this.no=p_no;
    10     this.say=function(){
    11         alert(this.name+this.age+this.no);
    12     }
    13 }
    14 Student.prototype = new Person('wangwu',21);
    15 var stu = new Student(10);
    16 stu.speak();
    17 stu.say();
  • 相关阅读:
    育碧-彩虹六号如何绑定二次验证码/两步验证/身份验证?
    leetcode刷题-95/96/98
    leetcode刷题-94二叉树的中序遍历
    leetcode刷题-93复原IP地址
    leetcode刷题-91解码方法
    leetcode刷题-90子集 II
    leetcode刷题-89格雷编码
    leetcode刷题-88.合并两个有序数组
    leetcode刷题-86分隔链表
    leetcode刷题-82.删除排序链表中的重复元素 II
  • 原文地址:https://www.cnblogs.com/platycoden/p/5213949.html
Copyright © 2011-2022 走看看