1.数据类型
javascript包含两种不同的数据类型的值:基本类型和引用数据类型。
1.1 基本类型
String、Number、Boolean、null、undefined、Symbol(es6)
1.2 引用类型
Object、data、Array、Set(es6)、Map(es6)等
1.3 基本类型和引用类型的区别
javascript的变量存储方式 栈内存(stack)和堆内存(heap)
栈:自动分配内存,系统自动释放。里面存放的是基本类型的值和引用类型的地址。
堆:动态分配内存,大小不定,也不会自动释放,里面存的是引用类型的值。
基本数据类型是按值访问的,因为可以操作保存在变量中的实际的值。
引用类型的值是保存在内存中的对象。javascript不允许直接访问内存中的位置,也就是说不能直接
操作对象的内存空间。在操作对象时:实际上是在操作对象的引用而不是实际的对象。
传值与传址
基本类型和引用类型最大的区别实际是 传值与传址的区别
值传递:基本类型采用的是值传递。
地址传递:引用类型则是地址传递,将存放在栈内存中的地址赋值给接受的变量。
在我们进行赋值操作的时候,基本数据类型的赋值(=)是在内存中开辟一段栈内存,然后再将值赋值到新的栈中
var a = 10;
var b = a;
a++;
console.log(a); //11
console.log(b); //10
//基本类型的赋值 两个变量是两个独立互不影响的变量
但是引用类型的赋值是传址。只是改变了指针的指向。
也就是说引用类型的赋值是对象保存在栈中的地址的赋值,这样的话两个变量就指向同一个对象,因此两者之间操作互相有影响。
var a = {};
var b = a;
a.name = "jane";
console.log(a.name,b.name) //jane,jane
b.age =18;
console.log(a.age,b.age)//18,18
console.log(a ===b);//true
占内存中的变量一般都是已经大小或者有范围上限的,算作一种简单存储。
而堆内存存储的对象类型数据对于大小这方面,一般都是未知的。
个人认为,这也就是为什么null作为一个object类型的变量却存储在栈内存中的原因了。
因此当我没定义了一个const对象的时候,我们说的常量其实是指针,就是说const对象对应的堆内存指向是不变的,但是堆内存中的数据本身的大小或者属性是可变的。
对于const定义的基础变量而言,这个值就相当于const对象的指针,是不可变的。
说到这里,有一个十分容易忽略的点,使用new关键字初始化之后是不存在栈内存中的。根据构造函数生成新实例,这个时间生成的是对象,而不是基本类型
var a = new String("123");
var b = String("123");
var c = "123";
console.log(a==b,a==c,b==c); //true,true,true
console.log(a===b,a===c,b===c); //false,false,true
console.log(typeof a ) //"object"
可以看出new一个String,出来的是对象,而直接字面量赋值和工厂模式出来的都是字符串。但是根据我们上面的分析大小相对固定可预期的几遍是对象也可以存储在栈内存的。比如null,为啥这个不是呢
var a = new String("123");
var b = new String("123");
console.log(a==b,a===b); //false false
很明显,如果a,b是存储在栈内存的话,两者明显应该是相等的,就像null === null是true一样,
但结果两者并不相等,说明两者都是存储在堆内存中的,指针指向不一致。
我们常说的值类型和引用类型其实说的就是栈内存和堆内存变量,再想想值传递和引用传递、深拷贝和浅拷贝,都是围绕堆栈内存展开的,一个是处理值,一个是处理指针。
主要区别
1.值是否可变
基本类型的值是不可变的,除非重新给他赋值,引用类型的值是可变的
var name = 'jozo';
name.toUpperCase(); //'JOZO'
name[0] = "mm";
console.log(name); //'jozo'
name = "song";
console.log(name); //"song"
var person = {};
person.name = "jozo";
console.log(person); //{name:"jozo"};
2.比较,是否2个数据类型相等
基本类型的比较是 值的比较
var a = 1;
var b = true;
console.log(a == b);// true
引用类型的比较是 引用的比较
var person1 = {};
var person2 = {};
console.log(person1 == person2); //false
3.值存放的位置
基本类型的变量是存放在栈内存的。
引用类型的值是保存在栈内存和堆内存中的对象。
复制的情况
复制时,基本类型是直接复制了一个新的变量,新旧两个变量之间没有关系
引用类型复制了新的变量,但这个变量是一个指针,新旧两个指针指向同一个对象。
var a = 10;
var b = a;
a++;
console.log(a); //11
console.log(b); //10
//基本类型的赋值 两个变量是两个独立互不影响的变量
var a = {};
var b = a;
a.name = "jane";
console.log(a.name,b.name) //jane,jane
b.age =18;
console.log(a.age,b.age)//18,18
console.log(a ===b);//true
内存分配和垃圾回收
一般来说栈内存线性有序存储,容量小,系统分配效率高。而堆内存首先要在堆内存新分配存储区域,之后又要把指向存储到栈内存中,效率相对就要低一些了。
垃圾回收方面,栈内存变量基本上用完就回收了,而堆内存中的变量因为存在很多不确定的引用,只有当所有调用的变量全部销毁之后才能回收。
1.4 声明变量
声明新变量的时,可以使用关键词 new 来声明起类型
var a = new String;
var b = new Number;
var c = new Boolean;
var d = new Object;
var e = new Array;
如何判断数据类型呢?
基本数据类型的检测: 大多使用typeof
console.log(typeof 2); //number
console.log(typeof "2"); //string
console.log(typeof null); //object
console.log(typeof undefined); //undefined
console.log(typeof function(){}); //function
console.log(typeof true); //boolean
引用数据类型检测 instanceof Object.prototype.toString.call();
instanceof并不能完全准确检查数据类型
A instanceof B 只是判断A的原型是不是B
let arr = [];
console.log(arr instanceof Array);//true
console.log(arr instanceof Object); //true
Object.prototype.toSting.call能更精准的区分数据类型
console.log(Object.prototype.toString.call("1")); //"[object String]"
console.log(Object.prototype.toString.call(1)); //"[object Number]"
console.log(Object.prototype.toString.call(true)); //"[object Boolean]"
console.log(Object.prototype.toString.call(undefined)); //"[object undefined]"
console.log(Object.prototype.toString.call(null)); //"[object Null]"
console.log(Object.prototype.toString.call({})); //"[object Object]"
console.log(Object.prototype.toString.call([])); //"[object Array]"
对象的创建
2.1 Object 类型构造函数
var obj = new Object();
var arr = new Array();
//添加方法
obj.name = "小明";
//添加属性
obj.sleep = function(){
console.log(this.name+"在睡觉")
}
2.2 字面量定义(嵌套字面量)
var obj1 = {};
obj1.name = "小红";
var obj2 = {
name:"小兰",
say:function(){
console.log(this.name+"说大家好");
}
}
2.3工厂定义方式
function createObj(name,age){
var obj = new Object();
obj.name = name;
obj.age = age;
obj.say = function(){
console.log(this.name+"说你好");
}
return obj;
}
var p1 = createObj("小明",20);
var p2 = createObj("小红",18);
2.4构造函数方式
构造函数方法 首字母大写
function Person(name,age){
this.name = name;
this.age = age;
this.say = function(){
console.log(this.name+"说你好");
}
}
var p1 = new Person("小明",20);
var p2 = new Person("小红",18);
2.5 Object.create
var p = {name:"小明"};
var obj = Object.create(p)
3.过程式开发与面向对象开发
面向过程 --站在一个执行者的角度去做事情;
面向对象 --站在指挥者的角度,是一种开发思想;
洗衣服作为例子:
面向过程:
1. 找个盆子
2. 收集要洗的衣服
3. 放水放洗衣液
4. 开始洗,洗完了晒
面向对象:
1. 找个对象
2. 让他去洗
3. 检查洗好了没,然后晾晒
面向对象的三大特征:
封装:
- 隐藏对象的属性和实现细节,仅对外提供公共的访问方式,将变化隔离,便于使用,提高复用性和安全性。
继承
- 提高代码复用性;继承是多态的前提。
多态
- 多态行是指相同的操作或函数、过程可作用于多种类型的对象上并获得不同的结果。不同的对象,收到同一消息可以产生不同的结果,这种现象称为多态性。
五大基本原则
单一职责原则
- 类的功能要单一,不能包罗万象。
开放封闭原则
- 一个模块对于拓展是开放的,对于修改是封闭的,想要增加功能热烈欢迎,想要修改,no!
接口分离模式
- 设计时采用多个与特定客户有关的接口比采用一个通用的接口要好。
最后
- 抽象会使复杂的问题更加简单化。
- 从以前面向过程的执行者,变成了指挥者。
- 面向对象更符合人类思维。面向过程则是机器的思想。