前端 javascript 常见面试题个人整理与总结

目录

基础题目

JS 变量按照存储方式区分为哪些类型,并描述其特点,区分值类型与引用类型的区别

JS 中使用 typeof 能得到哪些类型

强制类型转换,何时使用 === 何时 使用 ==

JS 中有哪些内置函数

如何理解 JSON

原型和原型链

涉及知识点

如何准确判断一个变量是数组类型

写一个原型链基础的例子

描述new一对象的过程

写一个封装DOM查询的例子


基础题目

JS 变量按照存储方式区分为哪些类型,并描述其特点,区分值类型与引用类型的区别

值类型:

引用类型: 

可以看出, 值类型把每一个值都放在一个内存上,引用类型改变了存放值的指向,如图此时a和b同时指向了该内存位置。

引用类型包括:对象、数组、函数,引用类型是为了让数据共用内存空间,减少内存占用

JS 中使用 typeof 能得到哪些类型

总结:可以得到 typeof 可以得到的类型有以下6种

  • undefined
  • number
  • string
  • boolean
  • object
  • function

注意:1.typeof null 返回的值是object 2.typeof 可以细致的区分值类型,而引用类型却不能细分,例如:数组和对象返回的都是object。

强制类型转换,何时使用 === 何时 使用 ==

以下4种情况会发生强制类型转换

  • 字符串拼接
var a = 10 + 10;   //20
var b = 10 + '10';  //'1010'
  • == 运算符
100 == '100' //true
0 == '' //true
null == undefined  //true

 使用 == 时候会发生强制类型转换。 第一行:将100 =》'100' 第二行:0 =》fasle  ' ' =》fasle 第三行同第二行

  • if 语句
var a = true
var b = 10
var c = ''
if(a){}  //true
if(b){}  //true
if(c){}  //false
  • 逻辑运算
10 && 0  //0
''|| 'abc'  //'abc'
!window.abc  //true

将一个常量转为true的方法。例:var a = 11; var b = !!a;  此时b的值就是true 

何时使用 === 和 ==

当判断出现

if(obj.a == null){
    //此时括号里的内容相当于 obj.a === null || obj.a === undefined
    //其余情况均匀用 ===
}

jquery源码推荐

JS 中有哪些内置函数

  1. Object
  2. Array
  3. Boolean
  4. Number
  5. String
  6. Function
  7. Date
  8. RegExp
  9. Error

如何理解 JSON

是javascript 中的一个对象而已,可以看作是一种数据格式

常用JSON.stringify({a:10,b:20})   <=> JSON.parse('{"a" : 10 , "b": 20}') 进行字符串《=》对象转换

 

原型和原型链

涉及知识点

参考文章  : javascript王国的一次旅行

  • 构造函数

在命名规则上,构造函数一般是首字母大写,普通函数则是遵照小驼峰式命名法

函数调用时,构造函数内部会创建一个实例,调用普通函数时则不会创建新的对象,构造函数内部的this指向是新创建的函数实例,而普通函数内部的this指向调用函数的对象(如果没有对象调用,默认为window)

 

  • 原型规则和实例

所有的引用类型(数组、对象、函数),都具有对象特性(即可以自由拓展属性)

所有的引用类型(数组、对象、函数),都有一个__proto__属性(可以称之为‘隐式原型’),属性值是一个普通的对象

所有的函数,都有一个prototype属性(可以称之为‘显式原型’),属性值也是一个普通的对象

所有的引用类型(数组、对象、函数),__proto__属性值指向它的构造函数“ prototype ”属性值

当试图得到一个对象的属性时,如果这个对象没有这个属性,那么会去找它的__proto__(即它的构造函数 prototype )中寻找

  • 原型链

在使用New方法初始化函数的时候(详细点击查看new的深度理解)得到的新对象的__proto__属性会指向函数对象的原型对象,而函数对象的原型对象又继承至原始对象。所以呈现以下结构

function fn(){};
var test = new fn();

把这个有__proto__串起来的直到Object.prototype.__proto__为null的链叫做原型链。

原型链实际上就是js中数据继承的继承链

  • instanceof

用于判断引用类型属于哪个构造函数的方法

如何准确判断一个变量是数组类型

var arr = [];
arr instanceof Array; // 正确答案,如果是数组,则会返回一个true
typeof arr;  // 错误答案,不管这个arr是数组还是对象,都会返回 object

写一个原型链基础的例子

function Book () {
  this.use = function () {
    console.log('read');
  }
}
function Mybook(){
  this.name = function(){
    console.log('css3权威指南');
  }
}
Mybook.prototype = new Book();  //指定Boox的继承函数为Mybook

var css3 = new Mybook();
css3.use();   //返回 read  说明Mybook已经继承了Book的.use方法

 js中的基础,可以用原型链来解释,因为原型链的存在,寻找函数总是通过__proto__属性来向上寻找,只要把这个属性更改为想要继承的函数,就可以继承该函数。

描述new一对象的过程

function Foo(name, age) {
  this.name = name;
  this.age = age;
  this.calss = 'calss_1';
  // return this;       这一行是自带的
}
var f = new Foo('Jack',12);
  1. 创建一个新对象
  2. this 指向这个新对象
  3. 执行代码,即对this赋值
  4. 返回 this

写一个封装DOM查询的例子

function Elem(id) {
  this.elem = document.getElementById(id);
}

Elem.prototype.html = function(val) {
  var elem = this.elem;
  if(val) {
    elem.innerHTML = val;
    return this;  //用来返回该对象进行链式操作
  }
  else {
    return elem.innerHTML;
  }
}

Elem.prototype.on = function(type,fn) {
  var elem = this.elem;
  elem.addEventListener(type,fn);
  return this;  //用来返回该对象进行链式操作
}

var div1 = new Elem('div1');
console.log(div1.html());
div1.on('click',function() {
  alert(1);
})

 

作用域和闭包

 

相关知识点

  • 执行上下文

 第一种情况

console.log(a);  //undefined
var a = 20; 

 这里我们先输出a,在定义a,输出值为undefined。

解释:js在运行时,先将a放到前面,此时赋值步骤是未执行得,所以a用undefined来代替,然后再输出,输出值则为undefined

第二种情况

fn('java')
function fn(name) {
    age = 20;
    console.log(name,age); //java 20
    var age;
}

 可以发现,先执行函数,再定义函数是可行的。在函数中,先赋值,再定义也是可行的。这种情况称之为执行上下文

每一个函数都有自己的执行上下文EC(执行环境 execution context),并且每个执行上下文中都有它自己的变量对象VO(Variable object),用于存储执行上下文中的变量 、函数声明 、函数参数,这解释了js如何找到我们定义的函数和变量。并且函数是js中唯一一个能创建出作用域的,注意:for,if()else()不能创建作用域。

  • this

this要再执行时才能确认值,定义时无法确认

var a = {
    name: "A",
    fn:function () {
        console.log(this.name);//如果这里内容错误,不执行得情况下,并不会报错,而执行时就会报错
    }
}
//在这行之前,a处于定义状态,此时的this不能定值,只有执行时,才能确定
a.fn();  //此时this === a;
a.fn.call({name:"B"}); //此时this === {name:"B"} 
var fn1 = a.fn;
fn1();  //此时的this === window

以下是4种执行情况

1、作为构造函数执行

2、作为对象属性执行

3、作为普通函数执行

4、call apply bind

function Foo(name) {
    this.name = name;
}
var f = Foo("zhangsan"); //作为构造函数执行

var obj = {
    name: 'name',
    myName: function () {
        console.log(this.name);
    }
}
obj.myName();  //作为对象属性执行

function fn() {
    console.log(this);
}
fn();

function fn1(name,age) {
    console.log(name);
    console.log(this);
}
fn1.call({x:100},"zhangsan",20);  //这里的{x:100}成了该函数的this
  • 作用域

javascript没有块级作用域(也就是for、if没有作用域的原因),它只有函数跟全局作用域

var a = 1;//这是全局
function fn() {
    var a = 2;  //这是函数作用域
}
  • 作用域链

什么是自由变量

var a = 1;
function fn() {
    var b = 2;
    console.log(a);    //此时在当前作用域内这个a没有被定义(此时它是一个全局变量),那么称这个a为自由变量
    console.log(b);
}
fn();

var a = 1;
function f1() {
    var b = 2;
    function f2() {
        var c = 3;
        console.log(a);  //a是自由变量
        console.log(b);  //b是自由变量
        console.log(c);
    }
    f2();
}
f1();

 上述代码中,可以看出,在f2中,a和b都是自由变量

而函数执行时,寻找变量是先从当前作用域(f2)找,没有的话向父级作用域(f1)中寻找,依然没有,继续向上寻找直到全局作用域,这样的过程我们称之为“作用域链”

  • 闭包

一个重要的一点:函数的父级作用域是在定义时就决定了,而不是在执行的时决定的(从某些方面看来和this的机制相反)

function fn() {
    var a = 1;
    return function () {
        console.log(a);
    }
}
var f1 = fn();
var a = 2;
f1();  //输出 1

 上端代码中,f1()执行是在全局执行的,他的父级作用域中重新给a赋值了2,但输出时,a仍然等于1

原因:在fn定义时,它的子函数的父级作用域定义了a=1,不管执行时函数在哪,此时的a永远等于1

闭包:在javascript中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解成“定义在一个函数内部的函数“。在本质上,闭包是将函数内部和函数外部连接起来的桥梁

闭包的使用场景

1、函数作为返回值

2、函数作为参数来传递

说一下对变量提升的理解

JavaScript 中,函数及变量的声明都将被提升到函数的最顶部。

JavaScript 中,变量可以在使用后声明,也就是变量可以先使用再声明。

说明this几种不同的使用场景

1、作为构造函数执行

2、作为对象属性执行

3、作为普通函数执行

4、call apply bind

创建10个<a>标签,点击的时候弹出来对应的序号

 第一张图的错误原因其实是因为js中的for不像java、c#那样有块级作用域,i是作为全局变量来使用的,所以第二张图加了一个闭包,把i限制在一个作用域里,这样alert出来的i就是正确的值

如何理解作用域

三大要点

自由变量

作用域链,即自由变量的查找过程

闭包的2个使用场景

实际开发中闭包的应用

在实际开发中,闭包主要是用来封装变量,收敛权限

function isFirstLoad(){
            var list=[];
            return function(option){
                if(list.indexOf(option)>=0){ //检测是否存在于现有数组中,有则说明已存在
                    console.log('已存在')
                }else{
                    list.push(option);
                    console.log('首次传入'); //没有则返回true,并把这次的数据录入进去
                }
            }
        }

var ifl=isFirstLoad();
ifl("zhangsan"); 
ifl("lisi");
ifl("zhangsan");

可以看到,如果外界想访问_list变量,只能通过我定义的函数isFirstLoad来进行访问,我对想访问_list的外界只提供了isFirstLoad这一个接口。至于怎么操作_list,我已经定义好了,外界能做的就只是使用我的函数,然后传几个不同的参数罢了。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值