LV2之-----js 值类型和引用类型 (摘录合集)------------基石1

一、学习目标
1.值类型和引用类型的基本概念
2.常见的值类型和引用类型都有哪些?
3.数据类型检测
4.值类型和引用类型在内存中的存储机制
5.创建对象的方式有哪些?
6.对象属性名的点表示法和方括号表示法
7.三种基本包装类型
二、概念
JS是弱类型语言,所谓弱类型只是表明该语言在表达式运算中不强制校验运算元的数据类型,而并不表明该语言是否具有类型系统。一般而言,JS的变量可从作用域角度分为全局变量和局部变量。
ECMAScript 变量可能包含两种不同数据类型的值,一种叫简单类型或者说是值类型,指的是简单的数据段;另一种叫复合类型或者说是引用类型,是由多个值构成的对象。

在JS的数据类型中,number、boolean、string、null和undefined 都属于值类型,而函数和对象属于引用类型
三、判断方式
判断一个值类型需要要到 typeof 操作符

var num = 123;
console.log(typeof num)
//number
var bool = true;
console.log(typeof bool )
//boolean
var str = 'xianyu';
console.log(typeof str )
//string

判断具体的引用类型需要用到 instanceof 操作符

var arr = [1,2,3]
var fuc = function(){}
var obj = {}
console.log(arr instanceof Array);
//true
console.log(fuc instanceof Function);
//true
console.log(obj instanceof Object)
//true

四、存储机制
思考下面的案例:

var num1 = 10;
		var num2 = num1;
		num2 -= 2;
		console.log(num1,num2);
		//10 8

		var arr1 = [1,2,3];
		var arr2 = arr1;
		arr2.push(4);
		console.log(arr1,arr2)
		//[1, 2, 3, 4]  [1, 2, 3, 4]

		var person1 = {
			name:'xianyu',
			age:23,
		}
		var person2 = person1;
		person2.name = 'xiaoxianyu';
		console.log(person1,person2)
		//{name: "xiaoxianyu", age: 23} 
		//{name: "xiaoxianyu", age: 23}

我们不难发现,同样是两个变量的赋值操作,赋值之后的两个数值之间没有任何联系了,对一个值的修改并不会影响到另一个值;而引用类型就不一样,任意修改其中一个值,另一个值也跟着改变了,造成这个现象的本质原因在于值类型和引用类型在内存中的存储机制是不同的。

(一) 基本数据类型存放于栈内存(stack),包括变量标识符和变量的值,基本类型赋值后开辟新的内存空间,两个变量互不影响 。

var a = 10;
  var b = a;

LV2之-----js 值类型和引用类型 (摘录合集)------------基石1

1.栈数据结构:先进后出、后进先出(LIFO——last in first out)
实例:类似于手枪的弹夹、杯子、网球桶

LV2之-----js 值类型和引用类型 (摘录合集)------------基石1
2.数组实现——js的栈数据结构
LV2之-----js 值类型和引用类型 (摘录合集)------------基石1
3.栈作用:栈在计算机中很是底层的东西,但是在js这种编程语言中,它的作用是 在编译语言的编译器和内存中保存变量、方法调用
函数和栈:
LV2之-----js 值类型和引用类型 (摘录合集)------------基石1

结果:先完成fun1 、再完成fun2。
最后一行fun2先调用,先入栈,然后fun2里面调用fun1,fun1再入栈,但是栈的出栈顺序是fun1先,所以先执行fun1

4.队列数据结构:先进先出(FIFO)
队列(queue),它的特性是先进先出,插入在一端,删除在另一端。就像排队一样,刚来的人入队(push)要排在队尾(rear),每次出队(pop)的都是队首(front)的人
LV2之-----js 值类型和引用类型 (摘录合集)------------基石1

实例:类似于羽毛球桶、公交车、水管

5.数组实现——队列数据结构
LV2之-----js 值类型和引用类型 (摘录合集)------------基石1
(二)引用类型存放于堆内存(heap)中,它的赋值是堆内存地址的引用(指针),所以两个变量指向的还是同一个对象,对任何一个的操作都会相互的影响。

var a = {};
var b = a; 

LV2之-----js 值类型和引用类型 (摘录合集)------------基石1
LV2之-----js 值类型和引用类型 (摘录合集)------------基石1
LV2之-----js 值类型和引用类型 (摘录合集)------------基石1
五、函数的参数
1.形参:函数定义的参数
2.实参:函数调用时实际传递的参数。
3.参数匹配是从左向右进行匹配。如果实参个数少于形参,后面的参数对应赋值undefined。
4.实参的个数如果多于形参的个数,可以通过arguments访问。
5.【案例】
(1)模拟原生max方法,只能比较两个

function max(a, b) {
  // for(var i in arguments)
  console.log(arguments);
  // 每个函数内部都可以直接访问arguments,里面就是存放着我们传递给函数的参数
  // arguments 不是一个数组。它的属性:0、1、2...
  // arguments有一个length属性,属性值就是传递实参数的个数。
  for(var i = 0; i < arguments.length; i++) {
    console.log(arguments[i]);  // 打印所有的参数。
  }

  if( a > b ) {
    return a;
  } else {
    return b;
  }
}
console.log(max.length); // 函数的length属性是指形参的个数。
var t = max(9,10);
console.log(t);
var m = max(9, 10, 20, 30);//这样还是只能比较前两个

(2)任意个比较

function myMax() {
  // 如果调用函数的地方没有传递参数,那么直接返回NaN
  if(arguments.length <=0 ) {
    return NaN;  // return语句执行,当前函数立即结束。
  }

  var max = arguments[0];
  // 获取实参:使用arguments,它是一个类数组。length属性是实参的个数。
  for(var i = 1; i < arguments.length; i++) {
    if(arguments[i] > max) {
      max = arguments[i];
    }
  }

  return max;
}
var m = myMax(10, 9, 2, 33, 22, 18);
console.log(m); // m => 33

6.函数对象的length属性就是函数形参的个数。

7.如果参数多于4个,那么开发人员很难记忆,最好将参数封装成对象来接受,对象的属性是无序的,可以方便开发人员使用。

// 一个函数:封装一个矩形。矩形:x y坐标, width、 height、背景色、文字信息、文字的坐标、文字字体颜色

// 全部用形参来处理矩形的参数
function drawRect(x,y,w,h,bg,text,textX,textY,textColor){
	var newNode=document.createElement('div')
	newNode.style.position='absolute'
	newNode.style.left=x+'px'
	newNode.style.top=y+'px'
	newNode.style.width=w+'px'
	newNode.style.height=h+'px'
	newNode.style.background=bg
	document.body.appendChild(newNode)
}

drawRect(20,20,100,200,'red')

// 如果函数的形参非常多问题:
// 1、开发人员很难记忆形参的具体情况
// 2、传递参数的时候,如果顺序不小心写错了,那么就会导致函数内部出现错误。
// 3、编写代码不方便。

// 解决办法:把这些形参封装成一个对象进行传递。
function rect2(rectObj) {
  // 拿到矩形的x、y坐标
  console.log(rectObj.x + ' ' + rectObj.y);
}

// rectObj
var rectObj = {
  x: 19,
  y: 20,
  width: 200,
  height: 300,
  bgColor: '#ccc',
  text: 'laoma'
};

rect2(rectObj);

六、函数参数的引用传递和值传递
JavaScript中所有函数的参数都是按值传递的!不存在按引用传递!
如果实参是值类型,会复制一个值类型的副本给函数,不会影响原来的传递参数的值类型变量。
如果实参是引用类型,传递只是引用类型的一个地址值,在函数内部操作参数对应的引;用对象会影响到传递的参数。

例1:值传递!
var num=10
function myshow(num){
	num=20
	console.log(num)
}
myshow(num)
console.log(num)

例2:引用传递?
var obj={
	age:20
}
function show(ccc){
	ccc.age=30
	console.log(ccc)
}
show(obj)
console.log(obj)

那么问题来了,为什么引用类型使用按值传递却会影响函数外对应的变量呢?

例3:按共享传递!!
 let a ={age:20},b={age :30};
   function changeAge(obj) {
        obj =  b
   }
   changeAge(a);
   console.log(a.age);//20

而共享传递是指,在传递对象的时候,传递对象的引用的副本。注意: 按引用传递是传递对象的引用,而按共享传递是传递对象的引用的副本!
这里在函数里将b赋值给a,应该是改变a的指针。但是结果a的指针并没有发生改变。所以对于对象而言,函数的按值传递的“值”,可以理解为对象的指针。不管在函数中如何操作,函数的指针都不会发生变化。这就是函数参数的按值传递。
所以修改 ccc.age,可以通过引用找到原值,但是直接修改例3中的 obj,并不会修改原值。所以第二个和第三个例子其实都是按共享传递。
其实也可以直接判断两个对象相等不,形参和传入的a对象
console.log(obj==a);
最后:
参数如果是基本类型是按值传递,如果是引用类型按共享传递。
但是因为拷贝副本也是一种值的拷贝,所以在高程中也直接认为是按值传递了。

参考文章:
https://www.cnblogs.com/zareb/p/5699571.html
https://www.cnblogs.com/dgjamin/p/4337677.html
https://blog.****.net/m0_38038235/article/details/76647867

七、创建对象实例的方式
创建Object实例的方式(目前先了解2种,面向对象再说):

1.使用new 操作符后跟Object构造函数。

var person = new Object();
person.name = 'xianyu';
person.age = 23;

2.对象字面量表示法:
	var person1 = {
		name:'xianyu',
		age:23
	}
当然数组也一样,因为数组也是一种对象
	var arr = new Array();
	var arr1 = [ ];

八、点表示法和方括号表示法
对于一个对象而言,我们可以通过点表示法和方括号表示法访问对象的属性。两者有如下区别:
(1)方括号表示法总是能代替点表示法,但点表示法却不一定能全部代替方括号表示法。
(2)方括号表示法可以用变量名作为属性名,点表示法不能。
(3)方括号表示法可以用纯数字为属性名,点表示法不能。
(3)方括号表示法可以用打空格的字符串为属性名,点表示法不能。

方括号表示法有较大的优势,只不过由于习惯和方便,我们一般用点表示法。

九、基本包装类型
我们都清楚,数值、布尔值和字符串是三种基本值类型,对于一个对象来说,它拥有属性和方法很正常。

	var person = {
			name:'xianyu',
			sayName : function () {
				console.log(this.name)
			}
		}
		person.sayName();
		//xianyu
但是同样的属性加到一个数值上就不对了,很显然,值类型不能定义属性
var num = 12;
		num.sayNum = function(){
			console.log(num)
		}
		num.sayNum();
//Uncaught TypeError: num.sayNum is not a function

不过万事都有例外:toString

var newNum = num.toString()
console.log(newNum)
//12
console.log(typeof newNum )
//string

在num里面居然定义了一个toString属性!这个属性是个函数,将数值12成功转换成了字符串‘12’。
这真的是个例外吗?答案是否定的

为了便于操作基本类型的值,ECMAScript提供了三个特殊的引用类型:Boolean、Number、String
其实上面的操作可以还原成下面的步骤:

var num = new Number(12);
	var newNum = num.toString();
	num = null;
	console.log(newNum)
	console.log(typeof newNum)

这就是基本包装类型:当基本数据类型比如数字调用了tostring方法后就自动转换为与其简单值相对应的引用数据类型来处理。调用tostring相当于来自于new Number()身上的方法,而不是值类型身上的方法。

经过包装类型的处理之后,基本的数值类型就变得和对象一样能调用方法了。
但是基本包装类型和引用类型还是有区别的:
使用new操作符创建的引用类型的实例,在执行流离开当前作用域之前一直保存在内存中。而自动构建的基本包装类型只存在于一行代码执行的瞬间,在此之后瞬间被销毁。这保证了我们不能在运行时为基本值类型添加属性和方法。

	var  s1 = "text";
	s1.font = 20;
	console.log(s1.font);
//undefined

相当于

	var s1 = new String('text');
	s1.font = 20;
	s1 = null;
	s1 = 'text';
	console.log(s1.font)
//undefined

另外,使用new操作符调用包装类型的构造函数与直接调用同名的转型函数结果是不一样的:

var num = '100';
var number = Number(num);
console.log(typeof number)
//number
var objNum = new Number(num);
console.log(typeof objNum)
//object

十、了解tostring() 和valueOf()

一篇文章搞懂toString() 和 valueOf()
https://blog.****.net/x_jagger/article/details/73430959
https://www.cnblogs.com/tincyho/p/9582680.html

练习:

练习:
var b = [1,2];

console.log(b+1);
console.log(b,b.toString());
console.log(b==b.toString());
console.log([1, 2]=="1,2");
console.log(b,b.valueOf());
console.log(b==b.valueOf());
console.log(b.toString()==b.valueOf());

var obj={}
console.log(obj+1);

var o = {
     valueOf: function(){
           return -1;
     }
};
console.log(o = +o);

解析:
//  数组+1返回 tostring+1
console.log(b+1);// 1,21
console.log(b,b.toString());//[1, 2]   "1,2"
console.log(b==b.toString());//true
console.log([1, 2]=="1,2");//true
console.log(b,b.valueOf());//[1, 2]    [1, 2]
console.log(b==b.valueOf());//true
console.log(b.toString()==b.valueOf());//true

var obj={}
console.log(obj+1);// 输出[object Object]1


var o = {
     valueOf: function(){
           return -1;
     }
};
console.log(o = +o);
valueOf()方法和toString()方法是一样的,都会在后台进行隐式的调用,在 o = +o时,等号右边就已经调用了valueOf(),相当于 o = +(-1);所以,最终结果o为-1.

----------------------------笔记简概JS Day10&11: --------------------
JS 的重点地基础( 面试常问 )
按值传递
函数的执行环境

作用域:起隔离变量的作用

js 的this指针
函数转成一个对象----工厂模式
构造函数
实例化对象
闭包:即函数是否出栈销毁
自执行函数
------------------------------------------------------疑点-
+1
++1 区别?
如何理解伪元素
关于函数提升
递归?
函数转成一个对象----工厂模式?
回调函数
for in
for each
图片懒加载