「JavaScript」 基础知识要点及常考面试题(一)

数据类型


原始类型有哪几种?null 是对象嘛?

  • null
  • undefined
  • string
  • boolean
  • number
  • symbol (es6 新增)

typeof null 输出的Object,但是对于js来说它还是作为一个基本类型
typeof :
原始数据类型的判断采用 typeof ,
原理: 不同的对象在底层都表示为二进制, 在 JavaScript 中二进制前三位都为 0 的话会被判断为 object 类型, null 的二进制表示是全 0, 自然前三位也是 0, 所以执行 typeof 时会返回“object”。
instanceof:
引用数据类型的判断采用 instanceof
原理:通过原型链去判断

原始类型和对象类型的区别

原始类型存储的是值,对象类型存储的是一个地址

  • 判断一个变量是否未定义
    首先如果单纯的使用未定义,浏览器会报错
    typeof xx == undefined
    用这个可以判断是否未定义

对象

for/in、Object.keys 和 Object.getOwnPropertyNames 对属性遍历有什么区别?你还知道其他遍历对象属性的方式吗?请说明。

ES6 一共有 5 种方法可以遍历对象的属性。

var parent = {}
Object.defineProperties(parent, {
    a: {
        value: 1,
        writable: true,
        enumerable: true,
        configurable: true
    },
    b: {
        value: 1,
        writable: true,
        enumerable: false,
        configurable: true
    },
    [Symbol('parent')]: {
        value: 1,
        writable: true,
        enumerable: true,
        configurable: true
    }
})
var child = Object.create(parent, {
    c: {
        value: 1,
        writable: true,
        enumerable: true,
        configurable: true
    },
    d: {
        value: 1,
        writable: false,
        enumerable: true,
        configurable: true
    },
    e: {
        value: 1,
        writable: true,
        enumerable: false,
        configurable: true
    },
    f: {
        value: 1,
        writable: true,
        enumerable: true,
        configurable: false
    },
    [Symbol('child')]: {
        value: 1,
        writable: true,
        enumerable: true,
        configurable: true
    }
})
  1. for...in
    for...in循环遍历对象自身的和继承的可枚举属性(不含 Symbol 属性)。
for (const key in child) {
    console.log(key)
}
// c d f a
  1. Object.keys(obj)
    Object.keys返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含 Symbol 属性)的键名。
Object.keys(child)
// ["c", "d", "f"]
  1. Object.getOwnPropertyNames(obj)
    Object.getOwnPropertyNames返回一个数组,包含对象自身的所有属性(不含 Symbol 属性,但是包括不可枚举属性)的键名。
Object.getOwnPropertyNames(child)
// ["c", "d", "e", "f"]
  1. Object.getOwnPropertySymbols(obj)
Object.getOwnPropertySymbols(child)
// [Symbol(child)]

Object.getOwnPropertySymbols返回一个数组,包含对象自身的所有 Symbol 属性的键名。

  1. Reflect.ownKeys(obj)
    Reflect.ownKeys返回一个数组,包含对象自身的所有键名,不管键名是 Symbol 或字符串,也不管是否可枚举。
Reflect.ownKeys(child)
// ["c", "d", "e", "f", Symbol(child)]

类数组转换为数组有哪几种实现方式?

类数组对象:只包含使用从零开始,且自然递增的整数做键名,并且定义了length表示元素个数的对象,我们就认为他是类数组对象!

var obj = {
    0: 'a',
    1: 'b',
    length: 2
}
console.log(Array.from(obj))
console.log(Array.prototype.slice.call(obj))
console.log([].slice.call(obj))

字符串

常见的字符串方法

  • str.charAt(index); 返回子字符串,index为字符串下标
  • str.charCodeAt(index); 返回字符串的unicode编码
  • str.indexOf(searchString,startIndex); 返回子字符串第一次出现的位置,从startIndex开始查找
  • str.lastIndexOf(searchString,startIndex); 从由往左找子字符串,找不到时返回-1
  • str.substring(start,end); 两个参数都为正数,返回值:[start,end) 也就是说返回从start到end-1的字符(有一点比较重要的是,start和end的参数是可以互换的,小的在前面,大的在后面)
  • str.slice(start,end); 两个参数可正可负,负值代表从右截取,(-1 指字符串的最后一个字符,-2 指倒数第二个字符,以此类推。) 返回值:(start,end) 也就是说返回从start到end-1的字符
  • str.substr(start,length); start参数可正可负,负数代表从右截取
    区别
  • str.split(separator,limit); 参数1指定字符串或正则,参照2指定数组的最大长度
  • str.replace(rgExp/substr,replaceText) 返回替换后的字符串
  • str.match(rgExp); 正则匹配, 匹配到则返回匹配的文字
    字符串比较
    字符串之间的比较:比较第一个字符的unicode编码值,第一个字符要是相同,就比较第二个

数组

常用的数组方法

数组常用方法.png
  • map() 方法按照原始数组元素顺序依次处理元素。
    注意:map() 不会对空数组进行检测。
    注意: map() 不会改变原始数组。
    删除数组的方法
  • splice
  • arr[1] delete[i] delete方法删除掉数组中的元素后,会把该下标出的值置为undefined,数组的长度不会变

会改变原来数组的有:
pop()---删除数组的最后一个元素并返回删除的元素。

push()---向数组的末尾添加一个或更多元素,并返回新的长度。

shift()---删除并返回数组的第一个元素。

unshift()---向数组的开头添加一个或更多元素,并返回新的长度。

reverse()---反转数组的元素顺序。

sort()---对数组的元素进行排序。

splice()---用于插入、删除或替换数组的元素。

map 与 forEach的区别

  • forEch 会改变原来的数组,而map不会
let arr = [1, 2, 3, 4, 5];

arr.forEach((num, index) => {
    return arr[index] = num * 2;
});
// arr = [2, 4,6,8,10]
arr.map((num,index)=> {
  return num * 2;
})
// arr = [1, 2, 3, 4, 5];

this


如何正确的理解this

  • 案例1 先来看一段函数调用
function foo() {
  console.log(this.a)
}
var a = 1
foo()

const obj = {
  a: 2,
  foo: foo
}
obj.foo()

const c = new foo()

分析:

  1. 在没有任何调用的情况, foo()的this是指向window
  2. obj.foo(), 谁调用this的指向就会指向谁
  3. new出来的对象,就绑定在c上
  • 案例2 bind
let a = {}
let fn = function () { console.log(this) }
fn.bind().bind(a)()

分析:
你们以为输出的结果是a?其实不然,bind函数所绑定都是由第一次绑定的值来决定,也就是fn.bind() => window

  • this有限级说明
    首先,new 的方式优先级最高,接下来是 bind 这些函数,然后是 obj.foo() 这种调用方式,最后是 foo 这种调用方式,同时,箭头函数的 this 一旦被绑定,就不会再被任何方式所改变。

闭包


关于闭包的理解

闭包是指,函数中调用函数,函数A中有函数B的存在,函数B中调用函数A的变量。

function A() {
  let a = 1
  window.B = function () {
      console.log(a)
  }
}
A()
B() // 1

用途. 使用闭包去处理var关键字的问题

for (var i = 1; i <= 5; i++) {
  ;(function(j) {
    setTimeout(function timer() {
      console.log(j)
    }, j * 1000)
  })(i)
}

缺点:

  1. 由于闭包中的变量都是存储在内存中,所有很容易导致内存泄漏
  2. 闭包中会改变父级作用域的变量值,如果把父函数当作一个对象,闭包函数当作公有方法,那么里面的成员变量就要慎用,因为闭包函数会改变其父类的变量,从而影响其他的调用

深拷贝和浅拷贝

对于深拷贝和浅拷贝的理解

  1. 浅拷贝
    可以使用扩展运算符(...)
let a = {
  age: 1
}
let b = { ...a }
a.age = 2
console.log(b.age) // 1
  • 可以使用Object.assign(a, {1,23})
  1. 深拷贝
  • 递归考虑
  • for 循环

原型链


原型链的一些要点

  • 如何访问
    1. 标准原型访问器Object.getPrototypeOf(object)
    2. 非标准访问器object.proto
    3. 通用访问器,object.constructor.prototype
  • 原型上的constructor指向函数本身
  • Object.prototype确实一个特例——它的proto指向的是null
  • 原型定义的一些公用的属性和方法;利用原型创建出来的新对象实例会共享原型(的所有属性和方法)。

下面这张图展现了原型链的整个模型

原型链

推荐文章 原型链精选

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 第3章 基本概念 3.1 语法 3.2 关键字和保留字 3.3 变量 3.4 数据类型 5种简单数据类型:Unde...
    RickCole阅读 5,163评论 0 21
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,148评论 1 32
  • 函数和对象 1、函数 1.1 函数概述 函数对于任何一门语言来说都是核心的概念。通过函数可以封装任意多条语句,而且...
    道无虚阅读 4,665评论 0 5
  • 请参看我github中的wiki,不定期更新。https://github.com/ivonzhang/Front...
    zhangivon阅读 7,191评论 2 19
  • 当大脑开始不听你指挥,新的东西再也无法停驻的时候,那个动手能力超强、无比聪明的奶奶,就变成眼前这个脑中只有旧事的奶...
    柳丹阅读 296评论 0 2