JS 进阶(3) ECMAScript5 — 面向对象:对象声明,对象遍历,闭包与封装,继承(原型继承、构造继承、call和apply继承),关键字(delete、this)

12 篇文章 1 订阅
本文详细介绍了JS中面向对象的实现,包括对象声明(字面式、构造函数等)、对象遍历、闭包和封装、继承(原型继承、构造继承、call和apply继承)以及面向对象的关键字delete和this。通过实例解析了各种继承方式和对象操作技巧。
摘要由CSDN通过智能技术生成

一、基本介绍

js 有两种开发模式:1.函数式(过程化),2.面向对象(OOP)。
面向对象的语言有一个标志,那就是类的概念。但是,js没有类的概念,因此它的对象也与基于类的语言中的对象有所不同。


二、声明对象

声明对象的几种方式

  1. 字面式
  2. new Object() 构造函数。
  3. 构造模式
  4. 工厂方式
  5. 原型模式声明对象
  6. 混合模式声明(构造模式+原型模式,平时开发最常用)

象格式为:先声明函数、函数内部使用new Object创建对象,并定义方法属性

2、原型模式创建对象格式为:创建一个空函数,函数外部使用prototype对象定义属性及方法

3、混合模式创建对象格式为:构造+原型

2.1 字面式(json模式)

字面式也是我们平时最常用的。

var person = {
	name: 'poorpenguin',
	age: 25,
	speak: function(say){
		alert(say);
	},
}

2.2 new Object() 构造函数

Object是所有对象的基类、根,所有的javascript对象都是由Object延伸的。

	var person = new Object();
	person.name = 'poorpenguin';
	person.age = 28;
	person.speak = function(){
		return '我是'+this.name+'!';
	}

2.3 构造方法

先声明函数,函数内使用this指向属性与方法。最后实例化函数。

	function Person(name,age){
		this.name = name;
		this.age = age;
		this.speakHi = function(){
			console.log('你好,我是'+this.name);
		};
	}

	var me = new Person('poorpenguin',25);
	me.speakHi();
	
	var you = new Person('aXiBa',23);
	you.speakHi();

2.4 工厂方式

工厂方式声明对象实际让就是对new Object()声明对象一系列方法的封装。
挺常用的,工厂模式就是为了解决不断的new,重复实例化的问题。

	function createObject(object){
		var obj = new Object();
		obj.name = object.name;
		obj.age = object.age;
		obj.addr = object.addr;
		obj.speak = function(){
			console.log('我是'+this.name+',今年'+this.age);
		};
		return obj;
	}

	var poorpenguin = createObject({name: 'poorpenguin',age: 25,addr: '中国'});
	poorpenguin.speak();

	var penguin = createObject({name: '不良企鹅', age: 25, addr: '利比亚'});
	penguin.speak();



2.5 原型模式声明对象

js中任何的方法都自带prototype这个对象。prototypeobject的子对象。
原型模式的根本就是函数本身声明空内容,利用prototype可以在函数外面定义一些属性和方法。

可以让所有实例化的对象都有prototype定义的属性和方法。

第一种写法

	function Person(){
	}
	Person.prototype.name = '默认';
	Person.prototype.age = 20;
	Person.prototype.init = function(name){	//初始化
		this.name = name;
		return this;
	}

	Person.prototype.speak = function(){
		console.log('我是'+this.name+',年龄'+this.age);
		return this;
	};

	var poorpenguin = new Person().init('poorpenguin');
	poorpenguin.speak();

第二种写法

		function Person(){

		}
		
		Person.prototype = {
			name: 'poorpengion',
			age: 25,
			speak: function(){
				console.log(this.name);
			}
		}

		var po = new Person();
		po.speak();

2.6 混合模式

混合模式是也是平时开发中最常用的。
混合模式就是原型+构造,属性使用构造模式定义在函数中,方法使用prototype定义在函数外。

	function Person(obj){
		this.name = obj.name || '未知';
		this.age = obj.age || 18;
		this.addr = obj.addr || '中国';
	}
	Person.prototype.speak = function(){
		console.log('我是'+this.name);
	}
	
	var man = new Person({name: 'poorpenguin',age: 25});
	man.speak();

三、对象遍历

对象有些时候可以当做数组去处理使用for in来遍历。

for in 可以遍历上面6种方法声明出来的对象

	function Person(obj){
		this.name = obj.name || '未知';
		this.age = obj.age || 18;
		this.addr = obj.addr || '中国';
	}
	Person.prototype.speak = function(){
		console.log('我是'+this.name);
	}
	
	var man = new Person({name: 'poorpenguin',age: 25});
	
	for(var p in man){			使用for in遍历
		console.log(man[p]);
	}

在这里插入图片描述


四、闭包和封装

其实这一段如果不是写底层的话一般可以不用太深入。

4.1 闭包

什么是闭包

全局域中无法直接操作局部作用域中的变量

n是在fn的函数作用域中,无法再全局作用域中操作n
	function fn(){
		var n = 1;
	}
	fn();
	n = 2;		这样是不行的,在全局域中无法直接操作局部作用域中的变量
	

但是在局部作用域中的可以操作局部作用域的变量

	function fn(){
		var n = 1;
		setN();				这样是可以的
		function setN(){
			n++;
		}
	}

所以我们将 局部作用域 中的函数 作为返回值 在全局作用域中使用不就可以了吗

	function fn(){
		var n = 1;
		function setN(){
			n++;
			console.log(n);
		}
		return setN;
	}
	
	fn()();		这样就实现了在全局作用域下,让局部作用域中的n自增

闭包的概念?

闭包是有权访问另一个函数作用域中的变量的函数,常见的方式就是在一个函数内部
再创建一个函数。

闭包的用途?

1.防止过多的全局变量,改成局部变量来定义。
2.外部函数始终在内存里不会被回收只有内部函数(闭包)被销毁后才被销毁。

闭包的优缺点?

1.优点:有利于封装代码,访问局部变量。
2.缺点:内存占用时间长,占用过多影响网页性能,IE中可能要内存泄漏。
注:内存泄露是指你用不到(访问不到)的变量,依然占居着内存空间,不能被再次利用起来。

闭包与变量

闭包只能取得包含函数中任何变量的最后一个值,所以在循环中的i只能保存最后一个。
解决方案:for循环里面函数想取到对应的i可以再加一个自执行的匿名函数把i当参数传入即可。



4.2 封装

封装就是把对象内部数据和数据操作细节进行隐藏,这是一种思想。

大多数面向对象的语言都支持封装的特性,提供了private关键字来隐藏某些属性,用来限制被封装的数据或者内容的访问。

js中没有专门的关键字,所以可以通过 闭包 来实现封装。因为函数内部声明的变量外部是访问不到的。

实现封装的思想的步骤:

  1. 创建一个构造函数(上面提到的那6种都行)
  2. 变量私有化:将需要私有化的变量,使用var在函数作用域中定义(ps:给对象添加的属性和方法无论如何都是不可能私有化的,我们要利用 函数作用域 进行私有化)
  3. 函数私有化:将需要私有化的函数,使用定义在函数作用域中。
  4. 为对象添加属性
  5. 为对象添加方法,通过对象的方法来操作私有化的变量和函数。

举个例子

function Student(){
	var privateStore = {};		全局作用域无法访问
	function _get(){			全局作用域无法访问
		return privateStore;
	}
	function _set(stu){			全局作用域无法访问
		if()
		privateStore[stu.name] = stu;
	}


	this.get = function(){		提供调用操作的接口,可以通过实例化出的对象调用
		return _get;
	};
	this.set = function(){
		return _set;
	};
}


var student = new Student();
student.set()({name:'poorpenguin',age: 18, sex: '男'});
console.log(student.get()());

student.set()({name:'mengfeng',age: 18, sex: '男'});
console.log(student.get()());


五、继承

  • 原型继承
  • 构造继承(对象冒充)
  • call继承
  • apply继承

5.1 原型继承

原型继承涉及到prototype_proto_

5.1.1 prototype 和 __ proto __ 的区别

prototype 原型: 只有函数才有的 属性,这个属性是一个指针,指向一个原型对象。原型对象是在定义函数的时候,浏览器默认创建的。

__ proto __ 原型链: 每个js对象都有的属性,指向它的构造函数的protoptype属性(也就是原型对象)。


5.1.2 __ proto __ 的指向

对象中的__ proto __属性指向该对象构造函数prototype属性

		function Person(){			构造方法
		}
		Person.prototype.name = '不良企鹅';
		Person.prototype.speak = function(){
			console.log('h1,guys');
		}
		
		console.log(Person.prototype);

		var people = new Person();
		console.log(people.__proto__);

		console.log(Person.prototype === people.__proto__); 

在这里插入图片描述

5.1.3 原型链

由于__proto__是任何对象都有的属性,包括prototype这个属性指向的对象也有__proto__这个属性,所以会形成一条__proto__连接起来的链条,链条最终指向null

	function Person(){}
	var people = new Person();

在这里插入图片描述
通过上面的图我们可以最终得出下面的原型链条

	console.log(people.__proto__  === Person.prototye);				true
	console.log(people.__proto__.__proto__  === Object.prototye);	true
	console.log(people.__proto__.__proto__.__proto__); 				null

5.1.4 原型继承

js的继承是发生在对象与对象之间。

每个构造函数都有一个属性 prototype 指向一个原型对象,该原型对象定义函数的时候,浏览器自动帮我们创建的。

将父类的实例作为子类的原型便是原型继承的核心。

		function Person(){
			this.type = '人类';
		}
		Person.prototype.run = function(){
			console.log('会跑');
		}
		Person.prototype.speak = function(){
			console.log('会说话');
		}

		function Chinese(){
			this.comeFrom = '中国';
		}
		Chinese.prototype = new Person();		继承了Person中的属性和方法
		
		Chinese.prototype.speak = function(){
			console.log('你好,世界!');
		}
		Chinese.prototype.sayType = function(){
			console.log('我是'+this.type);
		}
		Chinese.prototype.sayComeFrom = function(){
			console.log('我来自'+this.comeFrom);
		}


		var man = new Chinese();
		man.run();
		man.speak();
		man.sayType();
		man.sayComeFrom();

在这里插入图片描述




5.2 构造继承(对象冒充)

父对象被子对象继承,父对象的所有的 特权属性特权方法 都将传递到子对象中。

特权属性和特权方法就是在函数中定义的属性和方法

  • 使用this定义属性和方法。
  • 在构造函数使用function定义的方法和var定义的变量。

ps:在原型中定义的属性和方法,使用对象冒充(构造继承),子对象是无法继承。1

这才是构造继承,为什么很多人会把call、apply继承认作构造继承。。。

案例1:使用构造继承父类的特权属性和特权方法

	function Person(name){
		this.name = name;
		this.sayName = function(){
			console.log('我是'+this.name);
		}
	}

	function Student(name,str){
		this.pObj = Person;		冒充person对象,传递特权属性和特权方法
		this.pObj(name);
		this.str = str;
		this.sayHi = function(){
			console.log(this.str);
		}
	}

	var stu = new Student('学生','你好世界');
	stu.sayName();
	stu.sayHi();

案例2:使用构造继承(对象冒充)无法继承原型对象中定义的属性和方法

   	function Person(name){
   		this.name = name;
   		this.sayName = function(){
   			console.log(this.name);
   		}

   		this.sayHi = function(){
   			return hi;
   		}

   		function hi(){
   			console.log('函数作用域下定义的函数');
   		}
   	}
   	Person.prototype.test = function(){
   		console.log('这是在原型中定义的方法');
   	}


   	function Student(name){
   		this.pObj = Person;
   		this.pObj(name);
   	}

   	var stu = new Student('poorpenguin');
   	stu.sayName();
   	stu.sayHi()();
   	stu.test();

在这里插入图片描述




5.3 call继承

在子构造函数中 间接调用 想要继承的父构造函数,子构造函数将会获得父构造函数中的所有方法和属性。

	function Person(obj){
		this.name = obj.name;
		this.age = obj.age;
		this.speakName = function(){
			console.log('我是'+this.name+',年龄'+this.age);
		}
	}

	function Student(obj){
		Person.call(this,obj);		这个this是实例化中的stu这个对象,会将父构造函数中的所有this都指向stu。
	}

	var stu = new Student({name:'不良企鹅',age:25});
	stu.speakName();

在这里插入图片描述



5.4 apply继承

apply和call的用法几乎一样,就是传参有点不同

	function Person(name,age){
		this.name = name;
		this.age = age;
		this.speakName = function(){
			console.log('我是'+this.name+',年龄'+this.age);
		}
	}

	function Student(name,age){
		Person.apply(this,[name,age]);		
	}

	var stu = new Student('不良企鹅',25);
	stu.speakName();

六、面向对象中的一些关键字

1. delete

delete:只能删除对象中的属性,方法、原型链中的属性及变量不能被delete删除。

2. this

new实例化对象的时候,this代表的是当前对象。

   	var name = '不良企鹅';
   	function Person(){
   		this.name = 'poorpenguin';
   	}

   	var people = new Person();
   	console.log(people.name);	输出poorpenguin

如果只是函数调用的话,this表示的就是全局,和在函数外使用var声明的变量没有区别

    	var name = '不良企鹅';
    	function Person(){
    		this.name = 'poorpenguin';
    	}
    	Person();

    	console.log(name);	输出poorpenguin
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值