1.分类
(1).基本(值)类型(保存变量内存值的类型)
① String
② Number
③ boolean
④ undefined
⑤ null
(2). 对象 (引用) 类型(保存地址值)
① Object:任意对象
② Function:一种特别的对象(可以执行)
③ Array:一种特别的对象(数值下标,内部数据有序)
2. 判断
① typeof:返回的是数据类型的字符串表达,可以判断 undefined/ 数值/字符串/布尔值
可以判断:undefined / 数值 / 字符串 / 布尔值 / function
不能判断:null与object、object与array
② instanceof:判断对象的类型(具体类型)
定义: instanceof 用于检测构造函数的prototype属性是否出现在某个实例对象的原型链上;
语法:object instanceof constructor
object:某个实例对象
constructor :某个构造函数
③ ===: 可以判断undefined,null
// ① 基本类型
var a;
console.log(a, typeof a, type a ==='undefined', a === undefined) // undefined 'undefined' true true
console.log( undefined === 'undefined') // false
// ② 对象
var b1 = {
b2: [1, 'abc', console.log]
b3: function() {
console.log('b3')
return function() {
retuen 'xxxxxx'
}
}
}
cosnole.log(b2 instanceof Object, b1 instanceof Array) // true false
cosnole.log(b1.b2 instanceof Object, b1.b2 instanceof Array) // true true
cosnole.log(b1.b3 instanceof Function, b1.b3 instanceof Object) // true true
console.log(b1.b3()()) // xxxxxx
3. 相关问题
(1). 实例是什么?
实例:实例对象
类型:类型对象
function Persion(name,age) { // Persion是构造函数实际上是类型对象(函数)
this.name = name;
this.age= age;
}
① var p = new Persion('Tom', 12); // p 是根据类型创建的实例对象,其实调用的时候用new来调用才能确定他是构造函数,只不过创建的时候就算当作构造函数创建的,以为我们的函数名是大写的。
② Persion(‘Jack’,12); // 这种调用从语法上来说是符合的,但是从原则上来说是不应该的,毕竟函数进行创建的时候就算一构造函数的目的来创建的,所以调用的时候也要使用正确的调用方法
(2). undefined 和 null 的区别?
undefind:定义未赋值
null:定义并赋值,值为null
4.(1) 什么是数据:存储在内存中代表特定信息的“东西”,本质上是0101..
① 数据的特点:可传递、可运算;
② 算术运算、逻辑运算、赋值运算、运行函数
(2) 什么是内存:内存条通电后产生可存储数据的空间(临时的)
① 内存的产生和死亡:内存条(电路板) --> 产生内存空间 --> 存储数据 --> 处理数据 --> 断电 --> 内存空间和数据消失。
② 一块内存里有两个数据:内部存储的数据、地址值
③ 内存分类:
* 栈: 全局变量/局部变量、
* 堆:对象左侧是栈右侧是堆,栈里放变量,堆里放对象
(3) 什么是变量:可变化的量,由变量名和变量值组成;每个变量对应一块内存,变量名用来查找对应的内存,变量值就算内存中保存的数据;
5. 赋值和内存的问题:
var a = xxx, a内存中到底保存的是什么?
xxx是基本数据,保存的就是这个数据;
xxx是对象,保存的是对象的地址值;
xxx是一个变量,保存的是xxx的内存内容(可能是基本数据,也可能是地址值)
6. 引用变量赋值传递问题:
(1). 2个引用变量指向同一个对象,让其中一个引用变量指向另一个对象,另一引用变量依然指向前一个对象
var a = {age: 12};
var b = a ;
a = {name: 'Tom', age: 13};
console.log(b.age, b.name, a.age, a.name) // 12, undefined, 13, Tom
(2). 2个引用变量指向同一个对象,让其中一个引用变量指向另一个对象,另一引用变量依然指向前一个对象
function fn2 (obj) {
obj = { age: 15 }
};
fn2(a);
console.log(a.age); // 13 (obj 和 a 是两个对象,obj的age值改为15,不影响a的值)
(3). 2个引用变量指向同一个对象,通过一个变量修改对象内部的数据,另一个变量看到的是修改之后的数据
var obj1 = {name: 'Tom'};
var obj2 = obj2;
obj2.age = 12;
console.log(obj1.age); // 12
function fn (obj) {
obj.name = 'A'
};
fn(obj1);
console.log(obj.name); // A
7. 在js调用函数传递参数变量时,是值传递还是引用传递:
// 基本类型按值传递 // 换种写法
var a = 3; var a = 3;
function fn(a){ function fn(c) {
a = a + 1 b = c+1;
console.log(c,111) // 3
} }
fn(a); fn(a);
console.log(a); // 3 console.log(a,222); // 3
console.log(b,333); // 4
// eg2:
var a = 1;
function foo(val) {
val = 2;
}
foo(a);
console.log(a); // 输出结果为:1
// 引用类型按照对象传递(传递的是对象的地址值,其实也相当于一种值传递)
function fn2 (obj) {
console.log(obj.name)
}
var obj = {name: 'Tom'};
fn2(obj); //读到obj的(地址)值,然后把地址值赋值给形参,也就是函数内部的局部变量obj
// eg2:
var person = {name:'MJ'};
function foo(obj) {
obj.name = 'EP';
}
foo(person);
console.log(person.name); // 输出结果为:EP
对象person被修改了!这说明obj跟person指向的是同一个对象,可以肯定不是按值传递的了,那么就是引用传递了吗?从这个例子来看确实很像,我们再对上面的例子做下修改,如下:
// eg3:
var person = {name: "MJ"};
function foo(obj) {
obj = new Object(); // 修改部分
obj.name = "EP";
}
foo(person);
console.log(person.name); // 输出结果为:MJ
输出结果是MJ,也就是说person对象并未被修改,如此可以确定,js的引用类型也不是按引用传递。既不是按值传递,又不是按引用传递,那是按什么传递呢?
准确的说,JS中的基本类型按值传递,引用类型按共享传递(call by sharing,也叫按对象传递、按对象共享传递);该策略的重点是:调用函数传参时,函数接受对象实参引用的副本(既不是按值传递的对象副本,也不是按引用传递的隐式引用)。 它和按引用传递的不同在于:在共享传递中对函数形参的赋值,不会影响实参的值。
那为什么eg3中person没有被修改?
我们都知道对象保存在堆中,person持有堆中对象的引用,在调用foo函数时,函数接收的实际是person持有的对象(下面称之为原对象)引用的副本,这样obj和person都指向了同一个地方(也就是原对象在堆中存放的地址),而函数中的obj = new Object()操作其实是在堆中创建了一个新对象,并让obj持有该对象的引用,这样obj与原对象之间的关系就断开了,转而与新对象建立了关系,所以对obj的修改并不会影响到原对象。(本来obj和person指向的是同一个地址值,但是创建的新对象打断了obj之前的指向)