面试JS3

1vue生命周期Vue实例的生命周期详解 - 知乎 (zhihu.com)

一、什么是Vue生命周期

一个Vue实例从创建、运行到销毁期间,总是伴随着各种各样的事件,这些事件,就是Vue实例的生命周期。

二、生命周期函数

在整个生命周期过程中会有一些函数被自动调用,这些函数就叫做生命周期函数

常见的生命周期函数:

创建期间的生命周期函数:

1 beforeCreate(),创建前,实例刚在内存中被创建出来,此时,还没有初始化好data和methods属性。

2created(),创建后,实例已经在内存中创建完成。此时data和methods已经在内存中创建完成,但是还没有开始编译模板

3beforeMount(),挂载前,此时已经完成了模板的编译,但是还没有挂载到页面中显示

4mounted:挂载,此时,已经将编译好的模板,挂载到了页面指定的容器中显示

运行期间的生命周期函数:

5beforeUpdate:数据更新前,此时data中的状态值是最新的,但是界面上显示的数据还是旧的,因为此时还没有开始重新渲染DOM节点

6updated:数据更新后此时 data 中的状态值和界面上显示的数据,都已经完成了更新,界面已经被重新渲染好了!

销毁期间的生命周期函数:

7beforeDestroy:销毁前,在这一步实例仍然完全可用

8destroyed:销毁后,调用之后Vue实例指示的所有东西都会解绑,所有的事件监听器会被移除,所有的子实例也会被销毁。

2、什么是事件监听原生js——事件监听方法 - 简书 (jianshu.com)

一、传统事件绑定方法

最初接触的事件绑定方式大多是传统事件绑定方法。传统事件绑定方法事例如下:

window.onload=function(){
    alert("页面加载完毕");
}
document.getElementById("btn").onclick=function(){
    alert("按钮被点击");
}
document.onmousemove=function(){
    console.log("鼠标在移动");
}
  • 传统事件绑定方法的特点如下:

1、事件名称之间一定要加上on,比如onclick、onload,onmousemove

2、兼容主流的浏览器,包括低版本的IE

3、当同一个元素绑定多个事件时,只有最后一个事件会被添加,并且传播模式只能是冒泡模式

二、addEventListener()

方法实例:

window.addEventListener('load',init,false);
function init(){
    alert("页面加载成功");
}
// 下面写法与上面等价
window.addEventListener('load',function(){
    alert("页面加载成功");
},false);

浏览器兼容性:IE8及更早IE版本不支持addEventListener()方法,Opera7.0及Opera更早版本也不支持

addEventListener()方法特点:

1、element.addEventListener(event,function,useCapture)中的第三个参数可以控制指定事件是否在捕获或冒泡阶段执行。true-事件句柄在捕获阶段执行。false-默认-事件句柄在冒泡阶段执行

2、addEventListener()可以给同一个元素绑定多个事件,不会发生覆盖的情况,如果给同一个元素绑定多个事件,那么采用先绑定先执行的规则

3、addEventListener()在绑定事件的时候,事件名称之前不许带on

4、注意该方法的兼容性,如果要兼容IE6-8,不能使用该方法,可以采用以下方法

5、可以使用removeEventListener()来移除之前绑定过的事件

// 向 <div> 元素添加事件句柄
document.getElementById("myDIV").addEventListener("mousemove", myFunction);

// 移除 <div> 元素的事件句柄
document.getElementById("myDIV").removeEventListener("mousemove", myFunction);

三、attachEvent()

  • 方法事例:
window.attachEvent('onload',function(){
    alert("页面加载成功");
});
  • attachEvent()方法特点:
    1. attachEvent是 IE 有的方法,它不遵循W3C标准,而其他的主流浏览器如FF等遵循W3C标准的浏览器都使用addEventListener,所以实际开发中需分开处理。

    2. attachEvent()是 后绑定先执行

    3. 绑定时间时,attachEvent必须带 on,如 onclick,onmouseover

四、总结

  • 在我们实际的项目里面,在使用原生js绑定事件的时候,大多数情况下会使用 addEventListener() ,因为目前来说很少有人使用低版本IE了,大多数项目不会要求兼容 IE6-8
  • 如果项目要求兼容 IE6-8 ,这个时候可以考虑 attachEvent(),但是不建议这样使用,这样的话需要些兼容代码,不仅繁琐而且容易出错,这个时候就建议使用jQuery库直接进行事件绑定,jQuery已经帮我们做好了兼容处理的工作,直接饮用,提高效率。

3、介绍一下promise,及其底层如何实现Promise内部实现的原理 - 简书 (jianshu.com)

4、说说C++、Java、JavaScript这三种语言的区别说说C++,Java,JavaScript这三种语言的区别_SevenLee的个人博客-CSDN博客

从静态类型还是动态类型来看

静态类型,编译的时候就能够知道每个变量的类型,编程的时候也需要给定类型,如Java中的整型int,浮点型float等。C、C++\Java都属于静态类型语言

动态类型,运行的时候才知道每个变量的类型,编程的时候无需显示指定类型,如JavaScript中的var、PHP中的$。JavaScript、Ruby、python都属于动态类型语言

静态类型还是动态类型对语言的性能有很大影响

对于静态类型,在编译后会大量利用已知类型的优势,如int类型,占用四个字节,编译后的代码就可以用内存地址加偏移量的方法存取变量,而地址加偏移量的算法汇编很容易实现,

对于动态类型,会当做字符串统统保存下来,之后存取就用字符串匹配

从编译型还是解释型来看

编译型语言,像C、C++,需要编译器编译成本地可执行程序后才能运行,由于开发人员在编写完成后手动实施。用户只使用这些编译好的本地代码,这些本地代码由系统加载器执行,由操作系统的CPU直接执行,无需要其他额外的虚拟机等

源代码=>抽象语法树=>中间表示=>本地代码

解释型语言,像JavaScript、python,开发语言写好后直接将代码交给用户,用户使用脚本解释器将脚本文件解释执行。对于脚本语言,没有开发人员的编译过程,当然吗,也不绝对

源代码=>抽象语法树=>解释器解释执行

对于JavaScript,随着Java虚拟机JIT技术的引入,工作方式也发生了改变。可以将抽象语法树转成中间表示(字节码),再转成本地代码,如JavaScriptCore,这样可以大大提高执行效率。也可以从抽象语法树直接转成本地代码,如V8。

Java语言,分为两个阶段。首先像C++语言一样,经过编译器编译。和C++的不同,C++编译生成本地代码,Java编译后,生成字节码,字节码与平台无关。第二阶段,由Java的运行环境也就是Java虚拟机运行字节码,使用解释器执行这些代码。一般情况下,Java虚拟机都引入了JIT技术,将字节码转换成本地代码来提高执行效率。

注意,在上述情况中,编译器的编译过程没有时间要求,所以编译器可以做大量的代码优化措施。

对于JavaScript与Java它们还有的不同:

对于Java,Java语言将源代码编译成字节码,这个同执行阶段是分开的。也就是从源代码到抽象语法树到字节码这段时间的长短是无所谓的。

对于JavaScript,这些都是在网页和JavaScript文件下载后同执行阶段一起在网页的加载和渲染过程中实施的,所以对于它们的处理时间有严格要求。

5、js原型链,原型链的顶端是什么?Object的原型是什么?Object的原型的原型使什么?在数组原型链上实现删除数组重复数据的方法。js 原型链 - 简书 (jianshu.com)

什么是原型链?

每个对象都可以有一个原型_proto_,这个原型还可以有它自己的原型,以此类型,形成一个原型链。查找特定属性的时候,我们先去这个对象里去找,如果没有的话就去它的原型对象里面去找,如果还没有的话再去原型对象的原型对象里去寻找......这个操作被委托在整个原型链上,这个就是我们说的原型链了。

原型指针

我们知道了原型的概念,接下来我们就照着上面的图来具体分析一下原型的指针;中间最上面蓝色模块标注的构造函数Foo,里面有两个属性:_proto_和prototype,这两个很容易使人混淆,先说说prototype:

prototype:

prototype属性,它是函数所独有的,它是从一个函数指向一个对象。它的含义是函数的原型对象,也就是这个函数(其实所有函数都可以作为构造函数)所创建的实例的原型对象;这个属性是一个指针,指向一个对象,这个对象的用途就是包含所有实例共享的属性和方法(我们把这个对象叫做原型对象)。

__proto__:

__proto__是原型链查询中实际用到的,它总是指向prototype,换句话说,就是指向构造函数的原型对象,它是对象独有的。注意,为什么Foo构造也有这个属性呢?因为在js的宇宙里万物皆对象,包括函数;

根据以上的概括, 我们能知道Foo构造函数__proto__指向的是她的构造函数的原型对象,它的构造函数是Function,也就是说Foo的__proto__指向Function.prototype,我们再看到左边绿色的a和b函数的__proto__指向的是Foo.prototype,因为他们是通过new Foo实例化出来的,他们的构造函数就是Foo(),即a.__proto__=Foo.prototype;接着我们来看看最右边紫色的模块Function.prototype,它的__proto__指针指向的是Object.prototype,Object.__proto__又为null。于是我们就可以得出:在原型链中的指向是:函数->构造函数->Function.prototype->Object.prototype->null

constructor:

我们看到图中最中间灰色模块有一个constructor属性,这个又是做什么用的呢?

每个函数都有一个原型对象,该原型对象有一个constructor属性,指向创建对象的函数本身

此外,我们还可以使用constructor属性,所有的实例对象都可以访问constructor属性,constructor属性是创建实例对象的函数的引用。我们可以使用constructor属性验证实例的原型(与操作符instanceof非常类似)

由于constructor属性仅仅是原始构造函数的引用,因此我们可以使用该属性创建新的对象,如:

 通过第一个对象实例化对象的constuctor方法创建第2个实例化对象,说明创建的新对象ninja2 是Ninja的实例,由于ninja和ninja2不是同一个对象可以得出它们是两个截然不同的实例;

结论:

        1、__proto__ 是原型链查询中实际用到的,它总是指向 prototype;

        2、prototype 是函数所独有的在定义构造函数时自动创建,它总是被 __proto__ 所指。

所有对象都有__proto__属性,函数这个特殊对象除了具有__proto__属性,还有特有的原型属性prototype。prototype对象默认有两个属性,constructor属性和__proto__属性。prototype属性可以给函数和对象添加可共享(继承)的方法、属性,而__proto__是查找某函数或对象的原型链方式。constructor,这个属性包含了一个指针,指回原构造函数。

原型链顶端的原型对象:Object.prototype

一个例子让你彻底明白原型对象和原型链 - 简书 (jianshu.com) 

6、介绍一下symbol

symbol是ES6的新增属性,代表用给定名称作为唯一标识,这种类型的值可以这样创建,let id = symbol("id"),symbol确保唯一,即使采用相同的名称,也会产生不同的值,我们创建一个字段,仅为知道对应symbol的人能访问,使用symbol很有用,symbol并不是100%隐藏,有内置方法Object.getOwnPropertySymbol(obj)可以获得所有的symbol。

也有一个方法Reflect.ownKeys(obj)返回对象所有的键,包括symbol。

所以并不是真正隐藏。但大多数库内置方法和语法结构遵循通用约定他们是隐藏的,

7、promise+Generator+Async的使用Promise,Generator(生成器),async(异步)函数 - 最骚的就是你 - 博客园 (cnblogs.com)

8事件委托以及冒泡原理JS事件冒泡和事件代理(委托)_菜鸟搬砖记-CSDN博客_js事件委托和事件代理

9JS中string的startWith和indexof两种方法的区别

JS中startwith函数,其参数有3个,stringObj,要搜索的字符串对象,str,搜索的字符串,position,可选,从哪个位置开始搜索,如果以position开始的字符串以搜索字符串开头,则返回true,否则返回false

Indexof函数,indexof函数可返回某个指定字符串在字符串中首次出现的位置,

10js字符串转数字的方法js如何将字符串转数字?-js教程-PHP中文网

方法1、使用转换函数parseInt()或者parseFloat()

js提供了parseInt()和parseFloat()两个转换函数,前者把值转换成整数,后者把值转成浮点数。只有对String类型调用这些方法,这两个函数才能正常运行;对其他类型返回的都是NaN(not 啊Number)。

parseInt

根据JsPerf.com的基本测试,大多数浏览器对parseInt的响应最佳。虽然它是最快的方式,但使用parseInt会碰见一些常见陷阱:

parseInt("08"); // returns 0 部分老浏览器.

parseInt("44.jpg"); // returns 44

parseInt:没有传入基数时,默认是传入的基数为10 parseInt(num,10),如果不知道num属性的类型,不要使用parseInt进行字符串转数字。

parseFloat
如果你不解析 16 进制数,这是一个非常好的选择。例如:

parseInt(-0xff); // returns -255

parseInt("-0xFF"); // returns -255

parseFloat(-0xff); // returns -255

parseFloat("-0xFF"); // returns 0

注意:字符串中的负十六进制数字是一个特殊情况,如果你用 parseFloat 解析,结果是不正确的。为了避免程序出现 NaN 的情况,应该检查转化后的值。

parseFloat("44.jpg"); // return 44

parseFloat: 转换十六进制数时要小心,如果你不知道要转换对象的类型,不要使用 parseFloat。

方法2:使用Number()函数

Number() 函数把对象的值转换为数字;如果对象的值无法转换为数字,那么 Number() 函数返回 NaN。
示例:

var test1= new Boolean(true);

var test2= new Boolean(false);

var test3= new String("999");

var test4= new String("999 888");

 

document.write(Number(test1)+ "
");

document.write(Number(test2)+ "
");

document.write(Number(test3)+ "
");

document.write(Number(test4)+ "
");

输出:

1

0

999

NaN

方法3:使用js变量弱类型转换

var str= '012.345 ';

var x = str-0;

x = x*1;

11let const var的区别,什么是块级作用域,如何用ES5的方法实现块级作用域(立即执行函数),ES6呢?深入理解JS:var、let、const的异同 - forcheng - 博客园 (cnblogs.com)

var与let的区别

(1)作用域

用var声明的变量的作用域是它当前的执行上下文,即如果是在任何函数外面,则是全局执行上下文,如果在函数里面,则是当前函数执行上下文。换句话说,var声明的变量的作用域只能是全局或者整个函数块的。

而let声明的变量的作用域则是它当前所处代码块,即它的作用域可以是全局或者整个函数块,也可以是if,while,switch等用{}限定的代码块

另外,var和let的作用域规则都是一样的,其声明的变量只在其声明的块或子块中可用

function varTest() {
  var a = 1;

  {
    var a = 2; // 函数块中,同一个变量
    console.log(a); // 2
  }

  console.log(a); // 2
}

function letTest() {
  let a = 1;

  {
    let a = 2; // 代码块中,新的变量
    console.log(a); // 2
  }

  console.log(a); // 1
}

varTest();
letTest();

从上述实例中可以看出,let声明的变量的作用域可以比var声明的变量的作用域有更小的限定范围,更具灵活

(2)重复声明

var允许在同一作用域中重复声明,而let不允许在同一作用域中重复声明,否则会抛出异常

var相关示例代码

var a = 1;
var a = 2;

console.log(a) // 2

function test() {
  var a = 3;
  var a = 4;
  console.log(a) // 4
}

test()
varTest();
letTest();

let相关示例代码

if(false) {
  let a = 1;
  let a = 2; // SyntaxError: Identifier 'a' has already been declared
}


switch(index) {
  case 0:
    let a = 1;
  break;

  default:
    let a = 2; // SyntaxError: Identifier 'a' has already been declared
    break;
}

从上述示例中可以看出,let 声明的重复性检查是发生在词法分析阶段,也就是在代码正式开始执行之前就会进行检查。

(3)绑定全局对象

var在全局环境声明变量,会在全局对象里新建一个属性,而let在全局环境声明变量,则不会在全局对象里创建一个属性

示例代码:

var foo = 'global'
let bar = 'global'

console.log(this.foo) // global
console.log(this.bar) // undefined

那这里就一个疑问,let在全局环境变量不在全局对象的属性中,那它是保存在哪的呢?

var foo = 'global'
let bar = 'global'

function test() {}

console.dir(test)

在Chrome浏览器的控制台中,通过执行上述代码,查看 test 函数的作用域链,其结果如图:

test 函数的作用域链

由上图可知,let 在全局环境声明变量 bar 保存在[[Scopes]][0]: Script这个变量对象的属性中,而[[Scopes]][1]: Global就是我们常说的全局对象。

(4)变量提升与暂停死区

  1. var 声明的变量在执行上下文创建阶段就会被「创建」和「初始化」,因此对于执行阶段来说,可以在声明之前使用。

  2. let 声明的变量在执行上下文创建阶段只会被「创建」而不会被「初始化」,因此对于执行阶段来说,如果在其定义执行前使用,相当于使用了未被初始化的变量,会报错。

  3. 3let与const异同

  4. const 与 let 很类似,都具有上面提到的 let 的特性,唯一区别就在于 const 声明的是一个只读变量,声明之后不允许改变其值。因此,const 一旦声明必须初始化,否则会报错。

    示例代码:

    let a;
    const b = "constant"
    
    a = "variable"
    b = 'change' // TypeError: Assignment to constant variable
    

什么是块级作用域呢?

任何一对花括号({和})中的语句集都属于一个块,在这之中定义的所有变量在代码块外都是不可见的,我们称之为块级作用域。

ES5实现块级作用域:使用立即执行匿名函数ES5实现变量的块级作用域_Herrona的博客-CSDN博客

12关于setTimeout和Promise执行顺序问题 - sunmarvell - 博客园 (cnblogs.com)

13有了解过事件模型吗,DOM0级和DOM2级有什么区别,DOM的分级是什么DOM分级_拯救世界的光太郎-CSDN博客_dom分类

14平时怎么调试JS

一般用Chrome自带的控制台

15JS的基本数据类型有哪些,基本数据类型和引用数据类型的区别,NaN是什么的缩写,JS的作用域类型,undefined==null返回的结果是什么,undefined与null的区别在哪,写一个函数判断变量类型

JS的基本数据类型有字符串,数字,布尔,数组,对象,Null,Undefined,基本数据类型是按值访问的,也就是说我们可以操作保存在变量中的实际的值,

基本数据类型和引用数据类型的区别如下:

基本数据类型的值是不可变的,任何方法都无法改变一个基本类型的值,当这个变量重新赋值后看起来变量的值是改变了,但是这里变量名只是指向变量的一个指针,所以改变的是指针的指向改变,该变量是不变的,但是引用类型可以改变

基本数据类型不可以添加属性和方法,但是引用类型可以

基本数据类型的赋值是简单赋值,如果从一个变量向另一个变量赋值基本类型的值,会在变量对象上创建一个新值,然后把该值复制到为新变量分配的位置上,引用数据类型的赋值是对象引用,

基本数据类型的比较是值的比较,引用类型的比较是引用的比较,比较对象的内存地址是否相同

基本数据类型是存放在栈区的,引用数据类型同事保存在栈区和堆区

NaN是JS中的特殊值,表示非数字,NaN不是数字,但是他的数据类型是数字,它不等于任何值,包括自身,在布尔运算时被当做false,NaN与任何数运算得到的结果都是NaN,党员算失败或者运算无法返回正确的数值的就会返回NaN,一些数学函数的运算结果也会出现NaN ,

JS的作用域类型:

一般认为的作用域是词法作用域,此外JS还提供了一些动态改变作用域的方法,常见的作用域类型有:

函数作用域,如果在函数内部我们给未定义的一个变量赋值,这个变量会转变成为一个全局变量,

块作用域:块作用域吧标识符限制在{}中,

改变函数作用域的方法:

eval(),这个方法接受一个字符串作为参数,并将其中的内容视为好像在书写时就存在于程序中这个位置的代码,

with关键字:通常被当做重复引用同一个对象的多个属性的快捷方式

undefined与null:目前null和undefined基本是同义的,只有一些细微的差别,null表示没有对象,undefined表示缺少值,就是此处应该有一个值但是还没有定义,因此undefined==null返回false

此外了解== 和===的区别:

在做==比较时。不同类型的数据会先转换成一致后在做比较,===中如果类型不一致就直接返回false,一致的才会比较

类型判断函数,使用typeof即可,首先判断是否为null,之后用typeof哦按段,如果是object的话,再用array.isarray判断是否为数组,如果是数字的话用isNaN判断是否是NaN即可
扩展学习:

JS采用的是词法作用域,也就是静态作用域,所以函数的作用域在函数定义的时候就决定了,

看如下例子:

var value = 1;
function foo() {
console.log(value);
}
function bar() {
var value = 2;
foo();
}
bar();

假设JavaScript采用静态作用域,让我们分析下执行过程:

执行foo 函数,先从 foo 函数内部查找是否有局部变量 value,如果没有,就根据书写的位置,查找上面一层的代码,也就是 value 等于 1,所以结果会打印 1。

假设JavaScript采用动态作用域,让我们分析下执行过程:

执行foo 函数,依然是从 foo 函数内部查找是否有局部变量 value。如果没有,就从调用函数的作用域,也就是 bar 函数内部查找 value 变量,所以结果会打印 2。

前面我们已经说了,JavaScript采用的是静态作用域,所以这个例子的结果是 1。

16setTimeout(fn,100);100毫秒是如何权衡的

setTimeout()函数只是将事件插入了任务列表,必须等到当前代码执行完,主线程才会去执行它指定的回调函数,有可能要等很久,所以没有办法保证回调函数一定会在setTimeout指定的时间内执行,100毫秒是插入队列的时间+等待的时间

17怎么获得对象上的属性:比如说通过Object.key()获取object对象上的属性_靜哥哥的博客-CSDN博客

1、for...in...

let person = {
	name:"aaa",
	age:16
};
for (let x in person) {
	console.log(x); //name age
	console.log(person[x]); // aaa 16
}

for...in和for...of循环的区别

let a = ['A', 'B', 'C'];
a.name = 'Hello';
for (let x in a) {
	console.log(x); // '0','1','2','name'
}
for (let y of a) {
	console.log(y); // 'A', 'B', 'C'
}

for...in遍历的对象的属性名称,一个Array数组也是一个对象,它的每个对象的索引值被视为一个属性。当手动给Array对象添加额外的属性name后,for…in循环将把name包括在内,但Array的length属性却不包括在内。而for…of循环只循环集合本身的元素。

2. object.keys()
该方法是对象内置的属性方法,它返回一个数组,数组内包括对象内可枚举属性以及方法名称。数组中属性名的排列顺序和使用 for…in 遍历该对象时返回的顺序一致。

// 创建对象
let person = {
	name: "aaa",
	age: "16",
	sayHello: function() {
		console.log("Hello");
	}
};
// 通过调用Object.keys()方法,获取对象上已定义(可枚举)的属性和方法
let keys = Object.keys(person);
console.log(keys); 
//输出 (3) ["name", "age", "sayHello"]

3. Object.getOwnPropertyNames()
该方法返回一个指定对象所有自身属性的属性名(包括不可枚举属性但不包括Symbol值作为名称的属性)组成的数组。 数组中枚举属性的顺序与通过for...in循环(或Object.keys() 迭代该对象属性时一致,不可枚举属性的顺序未定义。

// 创建对象
let person = {
	name: "aaa",
	age: "16",
	sayHello: function() {
		console.log("Hello");
	}
};
// 通过调用Object.keys()方法,获取对象上已定义(可枚举)的属性和方法
let keys = Object.keys(person);
console.log(keys); 
//输出 (3) ["name", "age", "sayHello"]

嗯,基本就这样吧,后来一想其实是很基础很基础的东西了,竟然没想起来,最近真的要好好恶补基础了…

18如何使不同页面之间进行通信实现多个页面之间进行通信 - sunli0205 - 博客园 (cnblogs.com)

19private和public

public:public表明该数据成员、成员函数是对所有用户开放的,所有用户都可以直接进行调用

private:private表示私有,私有的意思就是除了class自己之外,任何人都不可以直接使用

20promise和await/async的关系

都是异步编程的解决方案

21js加载过程阻塞,解决方法

使用async (脚本相对于页面其他部分异步执行)或defer(等页面解析执行完再执行),

22js对象类型,基本对象类型以及引用对象类型的区别js 基本类型与引用类型的区别 - focusxxxxy - 博客园 (cnblogs.com)

基本的数据类型有:undefined,Boolean,number、string,null。基本数据类型的访问是按值访问的,就是说你可以操作保存在变量中的实际的值,基本类型有以下几个特点:

1、基本类型的值是不可变的:

任何方法都无法改变一个基本类型的值,比如一个字符串:

var name = 'jozo';

name.toUpperCase(); // 输出 'JOZO'

console.log(name); // 输出  'jozo'

会发现原始的name并未发生改变,而是调用了toUpperCase()方法后返回的是一个新的字符串

再来看一个:

var person = 'jozo';

person.age = 22;

person.method = function(){//...};

console.log(person.age); // undefined

console.log(person.method); // undefined

通过上面代码可知,我们不能给基本类型添加属性和方法,再次说明基本类型是不可变的

2、基本类型的比较时值的比较

只有在它们的值相等的时候它们才相等

但你可能会这样:

var a = 1;

var b = true;

console.log(a == b);//true

这是类型转换和==运算符的知识了,也就是说在用==比较两个不同类型的变量时会进行一些类型转换。像上面的比较先把true转换为数字1再和数字1进行比较,结果就是true了。这是比较的两个值的类型不同的时候==运算符会进行类型转换,但是两个值的类型相同的时候,即使是==也相当于是===

var a = 'jozo';

var b = 'jozo';

console.log(a === b);//true

3、基本类型的变量是存放在栈区的(栈区值内存里的栈内存)

假如有以下几个基本类型的变量:

var name = 'jozo';

var city = 'guangzhou';

var age = 22;

那么它的存储结构如下图:

栈区包括了变量的标识符和变量的值。

2引用类型

JavaScript中除了上面的基本类型之外就是引用类型了,引用类型也可以说是对象。对象是属性和方法的集合。也就说说引用类型可以拥有属性和方法,属性又可以包括基本类型和引用类型。

1、引用类型的值是可变的

可以为引用类型添加属性和方法,也可以删除其属性和方法

var person = {};//创建个控对象 --引用类型

person.name = 'jozo';

person.age = 22;

person.sayName = function(){console.log(person.name);} 

person.sayName();// 'jozo'

delete person.name; //删除person对象的name属性

person.sayName(); // undefined

上面代码说明引用类型可以拥有属性和方法,并且是可以动态改变的

2、引用类型的值是同时保存在栈内存和堆内存中的对象

JavaScript和其他语言不通,其不允许直接访问内存中的位置,也就是说不能直接操作对象的内存空间,那我们操作啥呢,实际上,是操作对象的引用,所以引用类型的值是按引用访问的。准确的说,引用类型的存储需要内存的栈区和堆区(堆区指内存里的堆内存)共同完成,栈区内存保存变量标识符和指向堆内存中该对象的指针,也可以说是该对象在堆内存的地址。

假如有以下几个对象:

var person1 = {name:'jozo'};

var person2 = {name:'xiaom'};

var person3 = {name:'xiaoq'};

则这三个对象的在内存中的保持情况如下图:、

3、引用类型的比较时引用的比较

var person1 = '{}';

var person2 = '{}';

console.log(person1 == person2); // true

上面讲到基本类型的比较的时候提到了当两个比较值的类型相同的时候,相当于是用===,所以输出是true了。再看看:

var person1 = {};

var person2 = {};

console.log(person1 == person2); // false

上面比较的是两个字符串,而下面比较的是两个对象,为什么长的一模一样的对象就不相等了呢?

引用类型时按引用访问的,话句话说就是比较两个对象的堆内存中的地址是否相同,那很明显,person1和person2在堆内存中地址是不同的

所以这两个是完全不同的对象,所以返回false

3、简单赋值

在从一个变量向另一个变量赋值基本类型时,会在该变量上创建一个新值,然后再把该值复制到为新变量分配的位置上: 

var a = 10;

var b = a;

a ++ ;

console.log(a); // 11

console.log(b); // 10

此时,a中保存的值是10,当使用a来初始化b时,b中保存的值也为10,但是b中的10与a中的是完全独立的,该值只是a中的值的一个副本,此后,这两个变量可以参加任何操作而相互不受影响

也就是说基本类型在赋值操作后,两个变量是相互不受影响的、

4、对象引用

当一个变量向另一个变量赋值引用类型的值时,统一也会将存储在变量中的对象的值复制一份放到为新变量分配的空间中。保存在变量中的是对象在堆内存中的地址,所以,与简单赋值不同,这个值的副本实际上是一个指针,而这个指针指向存储在堆内存的一个对象,那么赋值操作后,两个变量都保存了同一个对象地址,则这两个变量指向了同一个对象。因此,改变其中任何一个变量,都会互相影响: 

var a = {}; // a保存了一个空对象的实例

var b = a;  // a和b都指向了这个空对象

a.name = 'jozo';

console.log(a.name); // 'jozo'

console.log(b.name); // 'jozo'

b.age = 22;

console.log(b.age);// 22

console.log(a.age);// 22

console.log(a == b);// true

它们的关系如下图:

因此,引用类型的复制其实是对象保存在栈区地址指针的赋值,因此两个变量指向同一个对象,任何的操作都会相互影响。 

23JavaScript中的轮播实现原理?假如一个页面上有两个轮播,你会怎么实现?

图片轮播的原理就是图片排成一行,然后准备一个只有一张图片大小的容器,对这个容器设置超出部分隐藏,再控制定时器来让这些图片整天左移或右移,这样呈现出来的效果就是图片在轮播了。

如果有两个轮播,可封装一个轮播组件,供两处调用

24怎么实现一个计算一年中有多少周怎么实现一个计算一年中有多少周?_Benjamin的博客-CSDN博客_一年有多少周怎么算的

1、首先确定是不是闰年,也就是一年365天还是366天

2、确定当年1月1日是周几,假如是周五,一年265天就把1号2号3号减去,也就是把第一个不到一周的天数减去等于362,还得知道最后一天是周几,加入是周五,需要把周一到周五减去,也就是362-5=357.正常情况下257这个数计算出来是7的倍数,357/7=51.即为周数

25class

26EventLoop

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值