js 高频面试题详解
一:js 中的变量提升
例1
a = 2; var a; console.log(a);
答:2
解析:它会将当前作用域的所有变量的声明提升到程序的顶部,上述代码等价为:
var a; a = 2 console.log(a); // 2
例2:
console.log(a);// undefined var a = 2;
解析:变量的声明提升到程序的顶部;等价于:
var a; console.log(a); a = 2;
问题:为什么会有变量提升?
其实啊,js和其他语言一样,都要经历编译和执行阶段,而在编译的时候,会搜集所有的变量并且在本作用域内提前声明,而且其他代码都不会改变顺序。
1:作用域:除了函数外,js是没有块级作用域
2:作用域链:内部可以访问外部的变量,但是外部不能访问内部的变量。 注意:如果内部有,优先查找到内部,如果内部没有就查找外部的。
3:js的变量声明:js的变量声明其实大体上可以分为三种:var声明、let与const声明和函数声明。
函数声明与其他声明一起出现的时候,是以谁为准呢?答案就是,函数声明高于一切,毕竟函数是js的第一公民。
所以下面函数的调用会输出谁呢? 答案是 foo
foo(); function foo() { console.log('foo'); }
var foo = 2;
那么下面又会输出谁呢?
foo(); function foo() { console.log('1'); } function foo() { console.log('2'); }
答案是: 2 因为有多个函数声明的时候,是由最后面的函数声明来替代前面的。
那下列程序优惠输出什么呢?
foo(); var foo = function() { console.log('foo'); }
答案是报了Uncaught TypeError: foo is not a function 的异常
二:js 的作用域问题
1. 除了函数外,js是没有块级作用域。
2. 作用域链:内部可以访问外部的变量,但是外部不能访问内部的变量。
注意:如果内部有,优先查找到内部,如果内部没有就查找外部的。
3. 注意声明变量是用var还是没有写(window.)
4. 注意:js有变量提升的机制【变量悬挂声明】
5. 优先级:声明变量 > 声明普通函数 > 参数 > 变量提升
例1:
function c(){ var b=2 function a(){ console.log(b); // undefind var b=3 console.log(b); // 3 } a() console.log(b); // 2 } c()
例2:
var name = 'a'; (function(){ if( typeof name == 'undefined' ){ var name = 'b'; console.log('111'+name); }else{ console.log('222'+name); } })()
答案: 111b
例3:
function fun( a ){ var a = 10; function a(){} console.log( a ); } fun( 100 );
答案: 10
三:js的对象考题
例1:创建对象的三种方式
1:利用字面变量创建 var obj = { uname: '张三疯', age: 18, sex: '男', sayHi: function() { console.log('hi~'); } } 2:new object 创建对象 var obj = new Object(); // 创建了一个空的对象 obj.uname = '张三疯'; obj.age = 18; obj.sex = '男'; 3: 构造函数创建对象 // new 构造函数名(); function Star(uname, age, sex) { this.name = uname; this.age = age; this.sex = sex; this.sing = function(sang) { console.log(sang); } } var ldh = new Star('刘德华', 18, '男'); // 调用函数返回的是一个对象 // console.log(typeof ldh); console.log(ldh.name); console.log(ldh['sex']); ldh.sing('冰雨'); var zxy = new Star('张学友', 19, '男'); console.log(zxy.name); console.log(zxy.age); zxy.sing('李香兰') 注意: // 1. 构造函数名字首字母要大写 // 2. 我们构造函数不需要return 就可以返回结果 // 3. 我们调用构造函数 必须使用 new // 4. 我们只要new Star() 调用函数就创建一个对象 ldh {} // 5. 我们的属性和方法前面必须添加 this
例2:浅谈构造函数和对象
// 构造函数和对象 // 1. 构造函数 明星 泛指的某一大类 它类似于 java 语言里面的 类(class) function Star(uname, age, sex) { this.name = uname; this.age = age; this.sex = sex; this.sing = function(sang) { console.log(sang); } } // 2. 对象 特指 是一个具体的事物 刘德华 == {name: "刘德华", age: 18, sex: "男", sing: ƒ} var ldh = new Star('刘德华', 18, '男'); // 调用函数返回的是一个对象 console.log(ldh); // 3. 我们利用构造函数创建对象的过程我们也称为对象的实例化
例3:new关键字的执行过程
// new关键字执行过程 // 1. new 构造函数可以在内存中创建了一个空的对象 // 2. this 就会指向刚才创建的空对象 // 3. 执行构造函数里面的代码 给这个空对象添加属性和方法 // 4. 返回这个对象 function Star(uname, age, sex) { this.name = uname; this.age = age; this.sex = sex; this.sing = function(sang) { console.log(sang); } } var ldh = new Star('刘德华', 18, '男');
js对象的注意点:
1. 对象是通过new操作符构建出来的,所以对象之间不想等(除了引用外); 2. 对象注意:引用类型(共同一个地址); 3. 对象的key都是字符串类型; 4. 对象如何找属性|方法; 查找规则:先在对象本身找 ===> 构造函数中找 ===> 对象原型中找 ===> 构造函数原型中找 ===> 对象上一层原型查找
例3:[1,2,3] ==[1,2,3] // false [1,2,3] ===[1,2,3] // false
例4:
var obj1 = { a:'hellow' } var obj2 = obj1; obj2.a = 'world'; console.log(obj1); //{a:world} (function(){ console.log(a); //undefined var a = 1; })();
注意: js中的数据类型包含 基本数据类型和引用数据类型。
- 基本类型:string、number、boolean、undefined、null、symbol、bigint
- 引用类型:object
- NaN是一个数值类型,但是不是一个具体的数字
- 基本数据类型:基本类型值在内存中占据固定大小,数据直接存储在栈内存中
- 引用数据类型:引用类型在栈中存储了指针,这个指针指向堆内存中的地址,真实的数据存放在堆内存里。
- 具体介绍看:https://blog.csdn.net/song_Web/article/details/126659522
例5:
var a = {} var b = { key:'a' } var c = { key:'c' } a[b] = '123'; a[c] = '456'; console.log( a ); // {"[object Object]": "456"} console.log( a[b] ); // 456
四:JS作用域+this指向+原型的考题
1:原型:每一个构造函数都有一个 prototype 属性,指向另一个对象。这个 prototype 就是一个对象,这个对象的所有属性和方法,都会被构造函数所拥有。
2:对象原型_proto_:对象身上系统自己添加一个 __proto__ 指向我们构造函数的原型对象 prototype
3:实例对象原型( __proto__)和构造函数原型对象(prototype)里面都有一个属性 constructor 属性 ,constructor 我们称为构造函数,因为它指回构造函数本身
4: 原型链:每一个实例对象又有一个__proto__属性,指向的构造函数的原型对象,构造函数的原型对象也是一个对象,也有__proto__属性,这样一层一层往上找就形成了原型链。
5:对象成员的查找规则:当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性。如果没有就查找它的原型(也就是 __proto__指向的 prototype 原型对象)。如果还没有就查找原型对象的原型(Object的原型对象)。依此类推一直找到 Object 为止(null),按照原型链的方式去查找。
6:原型对象的this指向:
-
在构造函数中,里面this指向的是对象实例
-
原型对象函数里面的this 指向的是 实例对象
例1:
function Foo(){ getName = function(){console.log(1)} //注意是全局的window. return this; } Foo.getName = function(){console.log(2)} Foo.prototype.getName = function(){console.log(3)} var getName = function(){console.log(4)} function getName(){ console.log(5) } Foo.getName(); //2 getName(); //4 Foo().getName(); //1 getName(); //1 new Foo().getName();//3
例2:
var o = { a:10, b:{ a:2, fn:function(){ console.log( this.a ); // 2 console.log( this ); //代表b对象 } } } o.b.fn();
例3:
window.name = 'ByteDance'; function A(){ this.name = 123; } A.prototype.getA = function(){ console.log( this ); return this.name + 1; } let a = new A(); let funcA = a.getA; funcA(); //this代表window
例4:
var length = 10; function fn(){ return this.length + 1; } var obj = { length:5, test1:function(){ return fn(); } } obj.test2 = fn; console.log( obj.test1() ); //1 console.log( fn()===obj.test2() ); //false console.log( obj.test1() == obj.test2() ); //false
1. 原型可以解决什么问题
对象共享属性和共享方法
2. 谁有原型
函数拥有:prototype
对象拥有:__proto__
3. 对象查找属性或者方法的顺序
先在对象本身查找 --> 构造函数中查找 --> 对象的原型 --> 构造函数的原型中 --> 当前原型的原型中查找
4. 原型链
4.1 是什么?:就是把原型串联起来
4.2 原型链的最顶端是null