JavaScript面向对象进阶:Object类、静态成员、闭包原理与继承实现

作者:青柠时间:2026-10-30 04:32:59浏览:275

本文深入解析JavaScript面向对象编程核心概念。从Object基类的使用出发,详解静态属性与方法的模拟实现。重点剖析闭包的作用域链机制及其在保持变量内存中的应用,阐述私有属性的构造模式。最后对比call与apply的区别,并介绍扩展Object、构造函数借用及原型继承三种实现方式。

Object类与静态属性

1.Object类

在JavaScript中,Object是所有自定义类的基类。利用Object类创建对象时,无需显式定义构造函数,即可直接添加属性。


var per = new Object();
per.name = 'zhangsan';
per.age = ;
alert(per.name + per.age);

当程序需要一个能存储大量数据的对象变量时,直接使用Object类是一个简便选择,因为它避免了对构造函数的定义。此外,Object类提供的hasOwnProperty方法常用于检查对象自身是否包含特定属性。


var per = new Object();
per.name = 'zhangsan';
per.age = ;
if per.hasOwnProperty('email'){
alert('具有email');
}else{
alert('无email');
}

2.静态属性

在部分面向对象语言中,static关键字用于定义类的静态属性或方法。JavaScript虽无原生static关键字,但可通过特定语法模拟这一功能。

语法:

类名.属性名

类名.属性=function(){}


function Person(){
}
Person.count = ;
var p = new Person();
Person.count++;
var p = new Person();
Person.count++;
var p = new Person();
Person.count++;
alert(Person.count);

通过上述语法,可以为类添加静态属性和静态方法。


function Person(){
Person.count++; //静态属性
Person.getCount=function(){ //静态方法
alert('当前共有' + Person.count + '个人');
}
}
Person.count = ;
var p = new Person();
var p = new Person();
var p = new Person();
Person.getCount();

闭包原理与内存管理

3.闭包

闭包是指一个包含变量及其绑定环境的表达式(通常是函数),这些变量成为该表达式的一部分。

提出一个问题:


function display(){
var i=; 
}
display();
//在这里,想访问局部变量i

由于作用域不同,全局代码无法直接访问局部变量i。此外,当display函数执行完毕后,局部变量i通常会被垃圾回收机制释放。闭包的核心功能在于“访问局部变量”以及“防止变量所占内存被释放”。


//例
function fn(){
function fn(){
alert('hello');
}
return fn; //返回fn函数首地址
}
var test=fn(); //test也指向了fn函数的首地址
test();

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


//例
function fn(){
var i = ;
function fn(){
alert(i);
}
return fn; //返回fn函数首地址
}
var test=fn(); //test也指向了fn函数的首地址
test();

通过例2可知:若使用一个外部函数包含变量i,则局部变量i的内存将不会被回收。


//例
function fn(){
var i = ;
function fn(){
alert(i++);
}
return fn; //返回fn函数首地址
}
var test=fn(); //test也指向了fn函数的首地址
test();
test();
test();

在例3中,由于i的内存永远不会被回收,每次调用fn2时,i的值会自增1。运行结果依次弹出10、11、12。

闭包原理分析:在例3中存在三个作用域:全局作用域、fn1作用域和fn2作用域。全局作用域中执行test=fn1(),这相当于让test指向fn2。在fn1作用域内定义了var i=10并返回fn2;在fn2作用域内执行alert(i++)。当全局执行test=fn1()时,test指向了fn2的作用域,此时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:表示公有成员


function Person(p_name,p_age){
this.name = p_name;
var age = p_age;
}
var p = new Person('zhangsan',);
alert(p.name);
alert(p.age);

在示例中,若尝试用var声明私有成员属性,Person构造函数执行完毕后,age变量会被回收,无法直接作为成员属性使用。


function Person(p_name,p_age){
this.name = p_name;
var age = p_age;
this.setAge=function(a){
age = a;
}
this.getAge=function(){
return(age);
}
}
var p = new Person('zhangsan',);
p.setAge();
alert(p.getAge());

通过this.setAge和this.getAge两个方法引用局部变量age,使得age变量在函数执行后仍被引用,从而不被回收。

若仅存在set方法,表明该属性为只写属性。

若仅存在get方法,表明该属性为只读属性。

5.call和apply的使用

call和apply的功能是调用指定对象作为上下文的当前函数。两者功能完全相同,仅在语法形式上略有差异。

语法:

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

第一个参数:指定函数执行时this的指向对象

后续参数:按顺序指定传递给函数的参数

apply([thisObj[,argArray]])

第一个参数:指定函数执行时this的指向对象

第二个参数:数组,表示传递给函数的参数集合

在JavaScript中,函数有多种调用形式:


Person(); //Person内的this指向window
var p=new Person(); //Person内的this指向p
per.Person(); //Person内的this指向per
function Person(p_name,p_age){
this.name = p_name;
this.age = p_age;
}
function speak(){
alert(this.name + this.age);
}
var p = new Person('zhangsan',);
//speak(); 这样调用this指向window
//p.speak(); p对象没有speak属性

使用call和apply进行调用


function Person(p_name,p_age){
this.name = p_name;
this.age = p_age;
}
function speak(){
alert(this.name + this.age);
}
var p = new Person('zhangsan',);
speak.call(p);
speak.apply(p);

call和apply在执行时完成两件事:1)将函数内部的this指向第一个参数指定的对象;2)调用该函数。

另外,还可以采用以下方式解决问题:

P1.say=speak;

P1.say();

这种解决方式与前述方法有本质区别:

前述方法直接调用speak函数,仅改变函数内部this的指向。

上述方法会为p1对象增加属性,导致p1对象的体积变大。

举例说明:


<script>
function fn(){
this.style.color='red';
}
function fn(){
this.style.fontSize='px';
}
window.onload=function(){
document.getElementById('btn').onclick=function(){
var div = document.getElementById('div');
fn.call(div);
fn.apply(div);
};
};
</script>
<div id='div'>hello javascript</div>
<input type='button' id='btn' value='确定'>

JavaScript继承的三种实现

6.继承的三种实现方法

概念:在部分面向对象语言中,子类可继承父类的属性和方法。此功能可在JavaScript中进行模拟实现。

三种方法:

第一种:扩展Object方法


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

举例说明:


Object.prototype.ext=function(parObject){
//循环遍历父类对象所有属性
for(var i in parObject){
//为子类对象添加这个遍历到的属性
//它的值是父类对象这个属性的属性值
this[i] = parObject[i];
}
}
function Person(p_name,p_age){
this.name=p_name;
this.age=p_age;
this.speak=function(){
alert(this.name+this.age);
}
}
function Student(p_no){
this.no=p_no;
this.say=function(){
alert(this.no+this.name_this.age);
}
}
var stu = new Student();
stu.ext(new Person('xiaoqiang',));
stu.speak();
stu.say();

第二种:使用call和apply方法

语法:

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


function Person(p_name,p_age){
this.name=p_name;
this.age=p_age;
this.speak=function(){
alert(this.name+this.age);
}
}
function Student(p_no,p_name,p_age){
this.no=p_no;
this.say=function(){
alert(this.name+this.age+this.no);
}
Person.call(this,p_name,p_age);
}
var stu = new Student(,'zhagsan',);
stu.speak();
stu.say();

第三种:原型继承

语法:

子类.prototype = new 父类();


function Person(p_name,p_age){
this.name=p_name;
this.age=p_age;
this.speak=function(){
alert(this.name+this.age);
}
}
function Student(p_no){
this.no=p_no;
this.say=function(){
alert(this.name+this.age+this.no);
}
}
Student.prototype = new Person('wangwu',);
var stu = new Student();
stu.speak();
stu.say();

以上内容介绍了JavaScript面向对象编程中Object类、静态属性、闭包、私有属性、call和apply的使用,以及继承的三种实现方法,希望对大家有所帮助!

作者最新文章