原生JavaScript面向对象详解
![惊讶](http://static.blog.csdn.net/xheditor/xheditor_emot/default/ohmy.gif)
![大哭](http://static.blog.csdn.net/xheditor/xheditor_emot/default/wail.gif)
![害羞](http://static.blog.csdn.net/xheditor/xheditor_emot/default/shy.gif)
![奋斗](http://static.blog.csdn.net/xheditor/xheditor_emot/default/struggle.gif)
var People = {
name:””,
age:0,
eat:function(){
console.log(name + “吃饭了”);
}
}
上面就是一个模拟的人的类型(人:一种类型,一种数据类型,简称类。和整数类型、浮点数类型、字符串类型等等一样,就是一种数据类型),正常情况下要根据这个类型的模板来创建具体的实例对象!
var houZi = {}
houZi.name = “侯局长”;
houZi.age = 43;
houZi.eat = function() {
console.log(this.name + “吃饭了…”);
}
数据已经封装完成,将name、age、eat()作为一个人的属性和行为被封装在一起,也就是将零散的数据封装整合到一起方便程序中进行统一的管理和使用。
但是上面的封装方式使用的时候一定要注意适用范围
!比较适用于程序中某个类型只存在唯一实例对象的形式。
!不适合某个类型有多个对象的形式,多个实例书写方式就会很麻烦,实例和类之间没有关联关系、实例和实例之间没有关联关系
>>> 对象和类之间没有任何关系
>>> 同一个类的多个对象之间没有任何关系
> 这样原始的对象封装,在项目开发时,要使用在什么地方:
> 单例模式:当程序中,某个对象只能出现一次的时候。使用JSON对象直接封装
> 当程序中的某个类型可能出现多个对象的时候,不要使用这样的形式封装。
function People(name, age) {
return {
name:name,
age:age
}
}
在这样的情况下,创建对象就会变得非常简单
var leaderGao = People(“高经理”, 30);
var chaiQianChang = People(“拆迁常”, 35);
这样的优化,一定程度上降低了代码的复杂度,提高了代码的可用性。不过只是对上一种方式的封装,依然体现不出leaderGao、chaiQianChang实例和People类型之间的关系
3)构造函数
JavaScript提供了一种模拟面向对象的语法,通过函数的方式来模拟定义类型,定义好类型之后,通过new关键字来创建对象
定义类型的语法结构:
function People (name, age){
this.name = name;
this.age = age;
this.eat = function() {
console.log(this.name + “吃饭了…”);
}
}
此时,有了构造函数之后,我们对于创建对象有了新的看法
var jianChaJi = new People(“季检察长”, 50);
var chenHai = new People(“陈海”, 38);
我们重新审视创建的对象jianChaJi和chenHai
通过new关键字创建的对象,就会有自己的一个constructor属性,指向对象的构造函数,也就是我们定义的类型;通过这样的方式来反映类型和对象之间的关系
console.log(jianChaJi.constructor);
console.log(chenHai.constructor);
同样的,JS提供了另一个对象操作符号instanceof验证类型和对象之间的关系
console.log(jianChaJi instanceof People);
console.log(chenHai instanceof People);
程序是运行在计算中的进程,一旦程序要开始运行,必须要向操作系统申请内存空间以支持存取和计算程序运行过程中的各种数据。常规情况下,JS代码的运行,是基于浏览器引擎对象的。
运行JS的引擎,在程序运行时,向操作系统申请内存。内存会被划分成一下的几个部分
简要分析
栈区:由编译器自动分配释放 ,存放函数的参数值,局部变量的值等,内存的分配是连续的,类似于平时我们所说的栈,如果还不清楚,那么就把它想成数组,它的内存分配是连续分配的,即,所分配的内存是在一块连续的内存区域内.当我们声明变量时,那么编译器会自动接着当前栈区的结尾来分配内存.
堆区:一般由程序员分配释放, 若程序员不释放,程序结束时可能由操作系统回收.类似于链表,在内存中的分布不是连续的,它们是不同区域的内存块通过指针链接起来的.一旦某一节点从链中断开,我们要人为的把所断开的节点从内存中释放.
全局区:全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 程序结束后由系统释放
文字常量区:常量字符串就是放在这里的。 程序结束后由系统释放
程序代码区:存放函数体的二进制代码。
下面我将以两个具体事例来为大家分析
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script>
// 变量中存储的都是基本类型的数据
var x = 1;
var y = 12;
var z = x + y ;
console.log(z);
</script>
</head>
<body>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script>
// 变量中,如果存放的是对象
var People = function(name) {
this.name = name;
}
// People: 自定义数据类型——引用类型
// p1:引用变量
var p1 = new People("tom");
var p2 = new People("jerry");
console.log(p1 == p2);
var p3 = p1;// 做了什么事情?
console.log(p1 == p3);
p3.name = "shuku";
console.log(p1.name);
/**
* 1. 关于函数传递基本类型参数的问题
* 设置到栈内存操作
*/
function fn1(num) {
num = 12;
}
var x = 10;
fn1(x);
console.log(x);// 10
/*function fn2(num) {
num = 12;
return num;
}
var x = 10;
x = fn2(x);
console.log(x );// 12*/
/**
* 2. 传递引用类型的操作
*/
function fnx1(obj) {
obj.name = "damu";
}
var px = new People("tom");
fnx1(px);
console.log(px.name);// damu
function fnx1(obj) {// abcde1
obj = new People("damu");// qwerad
obj.name = "jerry";
}
var px = new People("tom");// abcde1
fnx1(px);// abcde1
console.log(px.name);// tom
</script>
</head>
<body>
</body>
</html>
字符串内存分析
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script>
var s1 = "hello";// 字面常量
var s2 = "hello";
var s3 = new String("hello");
var s4 = new String("hello");
console.log(s1 == s2);// true
console.log(s1 === s2);// true
console.log(s1 == s3);// true
console.log(s1 === s3);// false
console.log(s3 == s4);// false
console.log(s3 === s4);// false
console.log(typeof s1, typeof s3);
</script>
</head>
<body>
</body>
</html>
三、面向对象--继承
什么是继承:
在继承中,会出现两个类型【父类、子类,子类继承父类的属性和行为】
继承是用来做什么的:
继承是通过子类继承父类实现父类中代码的重复利用!
继承的使用注意事项:
尽量让一个父类有一个或者多个子类
一个子类只继承一个父类
继承的第一种方式:通过apply/call实现假继承
apply/call方法都是通过改变函数中第一个参数的指向来达到扩展代码功能的目的
唯一的区别就是apply和call后面的参数不一样!
var Cat = function(name, color, age, type) {
// Animal.apply(this, [name, color]);
Animal.call(this, name, color);
this.age = age;
this.type = type;
}
Cat.prototype.caches = function() {console.log("抓老鼠..");}
var cat = new Cat("tom", "黑色", 48, "猫科");
console.log(cat);
继承的第二种方式:通过prototype和空对象进行间接继承
var _extends = function(Child, Parent) {
var F = function() {};
F.prototype = Parent.prototype;
Child.prototype = new F();
Child.prototype.constructor = Child;
}
var Animal = function(name,age) {
this.name = name;
this.age = age;
}
Animal.prototype.play = function() {console.log("play gaming!");}
var Cat = function(name, age, color) {
this.color = color;
}
_extends(Cat, Animal);
var cat = new Cat();
console.log(cat);
三、面向对象--多态
多态:运行的过程中,对象在不同的环境下回表现出不同的状态(这个以后我会再加详细解释^-^)