变量
文章大纲
1.变量类型
js的变量类型有两种
-
基本类型: null undefined number boolean string symbol(ES6)
是存放在栈中的简单数据段,按值储存,所以可以按值访问,大小由类型确定,内存空间大小可以分配
-
引用类型: object(function array 都是基于object拓展出来的 typeof arr == ‘object’,涉及到原型链部分)
数据存放在堆中,在栈中保存对象的引用地址
深拷贝 浅拷贝
-
浅拷贝
即复制引用类型的数据时,只是简单的将地址拷贝过来,两块地址指向同一块堆内存,所以改a即改b
实现方式
var a = { key1: { m:123, h:33 }, key2: 'b' } var b = {}; /*对于对象来说*/ //1.直接拷贝对象属性 function copy(x,y){ Object.keys(x).forEach( (key) => { y[key] = x[key]; } ) } let arr = [1,{ my: 'dog' } ]; let arr2; copy(arr,arr2); arr2[1].my = 'asaa'; console.log(arr2); //2.调用对于assign函数 对于只有一层的对象进行深拷贝,但是对于第二层 第三层会复制对象的地址 b = Object.assign({},obj) //如a.key2 会被深拷贝, a.key1 会被浅拷贝 /*对于数组来讲*/ //1.Array.prototype.concat() // 连接空值返回一个浅拷贝数组 let arr = [1,{ my: 'dog' } ]; let arr2 = arr.concat(); arr2[1].my = 'pig'; console.log(arr); //2.Array.prototype.slice() // 切片从0开始 返回一个浅拷贝数组 let arr = [1,{ my: 'dog' } ]; let arr2 = arr.slice(0); arr2[1].my = 'pig'; console.log(arr);
-
深拷贝
如果我们想要两者不建立联系,则可以直接复制属性
var a = { key1: { m:123, h:33 }, key2: 'b' } var b = {}; //对于只有一层的对象 直接用assign函数 b = Object.assign({},obj); //使用递归进行实现 function copy(x){ //判断是否为数组 let y = Array.isArray(x)?[] : {}; for(let i in x){ //判断当前项是否为对象 如果是则深复制 if(typeof x[i] === 'object'){ y[i] = copy(x[i]); } //如果否 则浅复制 else{ y[i] = x[i]; } } return y } b = copy(a); a.key1.m = 'c'; console.log(b);
2.变量提升
解释(javascript并不是严格的自上而下执行的语言。)
当没有定义变量时,输出会报错,当定义变量时,当前变量会提升到可执行区域的最上方,但暂时不进行赋值,且当变量和函数重名时,函数的优先级高于变量。
即 函数自动提升到最上方; 变量的定义提升到最上方,但是赋值语句不变
原因
js在编译阶段会搜集所有的变量声明,并提前进行声明,但不会改变其他语句的执行顺序。
1.变量的定义
即 var a = 2;等价于 var a;(编译阶段) a=2(运行阶段)
2.当函数出现时
- 当函数和变量名相同时,函数是js的第一公民,所有以函数名为准
- 当函数定义重复时,以后边的为准,即后面的声明替换掉前面的声明
- 对于函数表达式 var x = function () { … } 与对变量的定义相同,报error,var x; x = function(){}
3.let/const 出现的情况
let/const 统一也会进行变量提升,但是在执行对应定义语句之前,是不能进行访问的,所以提前输出会出现not defined的状况,即代码编译到let/const执行定义之前,对应变量无法访问,称为时间死区。
一些题目
- 1
function f(a){
console.log(a);//(){}
var a = 2;
console.log(a);//2
function a(){};
console.log(a);//2
}
f(1);
/* 等价于
function f(a){
var a;
var a = (){};//函数提升到这一部分
console.log(a);
a = 2;
console.log(a);
console.log(a);
}
f(1); */
- 2
function test() {
console.log(a); // undefined
console.log(foo()); // 2
var a = 1;
function foo() {
return 2;
}
}
test();
/* 等价于
function test() {
var a;
function foo() {
return 2;
}
console.log(a); // undefined
console.log(foo()); // 2
a = 1;
}
test();*/
- 3 注意 foo = (){ … } 但是 foo() = 函数体里的内容
function test() {
console.log(foo); // (){}
console.log(bar); // undefined
var foo = 'Hello';
console.log(foo); // 'hello'
var bar = function () {
return 'world';
}
function foo() {
return 'hello';
}
}
test();
/* 等价于
function test() {
var foo;
var bar;
function foo() {
return 'hello';
}
console.log(foo);
console.log(bar);
foo = 'Hello';
console.log(foo);
bar = function () {
return 'world';
}
}
test();
思考
为什么string是基本类型,但是还可以用indexof或者其他的一些方法
原始值被当作构造函数创建的一个对象来使用时, JS 会将其转换为一个对象,以便其可以使用对象的特性(如方法),而后抛弃对象性质,并将它变回到原始值
基本包装类型 的概念。除了 Object Array 等引用类型外,其实还有三种特殊的引用类型 String Number 和 Boolean,方便我们操作与其对应的基本类型,而它们就是基本包装类型。str 作为一个基本类型是没有 length 属性的,但是它的基本包装类型 String 有啊,其实在执行 console.log(str.length) 这段代码时,事情的经过是这样的:
创建String类型的一个实例
在实例上调用指定的方法
销毁这个实例// java 中的拆箱封箱操作,基本类型和引用类型转换,js中省略了这一步
let str = "1353";
console.log(str.length);
// 等于
let str_obj = new String(str);
str_obj.length;
str_obj = null;
最后引用一段《Javascript启示录》中的话:
在针对字符串、数字和布尔值使用字面量时,只有在该值被视为对象的情况下才会创建实际的复杂对象。换句话说,在尝试使用与构造函数关联的方法或检索属性(如var len = ‘abc’.length) 之前,一直在使用原始数据类型。当这种情况发生时,Javascript 会在幕后为字面量值创建一个包装器对象,以便将该值视为一个对象。调用方法以后,Javascript 即抛弃包装器对象,该值返回字面量类型。这就是字符串、数字、布尔值被认为是原始数据类型的原因。
既然string有基本包装对象,那么numer是否有原型呢,写出可以正确执行 console.log((5).add(4).minus(8))的操作
Number.prototype.add = function(number){
if(typeof number !== "number"){
return new Error("输入数字");
}
return this.valueOf() + number;
}
Number.prototype.minus = function(number){
if(typeof number !== "number"){
return new Error("输入数字");
}
return this.valueOf() - number;
}
那如何实现Calc(2).add(5).minus(6)
借用上面的基本包装类型,然后进行使用
Number.prototype.add = function(number){
if(typeof number !== "number"){
return new Error("输入数字");
}
return this.valueOf() + number;
}
Number.prototype.minus = function(number){
if(typeof number !== "number"){
return new Error("输入数字");
}
return this.valueOf() - number;
}
Number.prototype.print = function(){
console.log("result"+this.valueOf());
}
console.log((5).add(4).minus(8));
function Calc(n){
this.result = n;
return this.result;
}
Calc(2).add(4).minus(5).print();
也可以通过把Calc当做入口函数,在内部封装具体prototype,然后new 一个对象 暴露原型对象;
unction Calc(n){
function calc(){
this.result = n;
}
calc.prototype.add = function(x){
this.result += x;
return this;
}
calc.prototype.minus = function(y){
this.result -= y;
return this;
}
calc.prototype.print = function(){
console.log('result='+this.result);
}
return new calc(n);
}
Calc(2).add(4).minus(5).print();
https://www.cnblogs.com/Lumia1020/p/5321618.html