1. 原始值和引用值类型及区别

原始值,也叫基本类型:如null,undefined,string,number,boolean
引用值,如Object,Function,Date,Array,RegExp
原始值与引用值的区别:

  • 原始值存储在中,引用值存储在
  • 原始值是以值的拷贝方式进行赋值,值是不可变的;
  • 引用值是以引用的拷贝方式进行赋值,值是可变的
  • 原始值的比较是值的比较,引用值的比较是引用的比较(比较引用的是否为同一对象)

    2.判断数据类型的常用方法

  • 基本数据类型(Undefined、Null、Boolean、Number、String)

  • 复杂数据类型 (Object)
    1. var bool = true
    2. var num = 1
    3. var str = 'abc'
    4. var und = undefined
    5. var nul = null
    6. var arr = [1,2,3]
    7. var obj = {name:'haoxl',age:18}
    8. var fun = function(){console.log('I am a function')}

    typeof

    typeof 进行类型判断的返回值有: ‘undeinfed’,’string’,’number’,’boolean’,’object’,’symbol’,’function’
    1. console.log(typeof bool); //boolean
    2. console.log(typeof num);//number
    3. console.log(typeof str);//string
    4. console.log(typeof und);//undefined
    5. console.log(typeof nul);//object
    6. console.log(typeof arr);//object
    7. console.log(typeof obj);//object
    8. console.log(typeof fun);//function
    typeof 对 null 返回 ‘object’,对正则数组返回 ‘object’

    instanceof

    用于检测某个对象的原型链(__proto__)上是否存在另一个对象的 prototype ```javascript console.log(bool instanceof Boolean);// false console.log(num instanceof Number);// false console.log(str instanceof String);// false console.log(und instanceof Object);// false console.log(arr instanceof Array);// true console.log(nul instanceof Object);// false console.log(obj instanceof Object);// true console.log(fun instanceof Function);// true

var bool2 = new Boolean() console.log(bool2 instanceof Boolean);// true

var num2 = new Number() console.log(num2 instanceof Number);// true

var str2 = new String() console.log(str2 instanceof String);// true

function Person(){} var per = new Person() console.log(per instanceof Person);// true

function Student(){} Student.prototype = new Person() var haoxl = new Student() console.log(haoxl instanceof Student);// true console.log(haoxl instanceof Person);// true

  1. 从结果中看出instanceof不能区别undefinednull,而且对于基本类型如果不是用new声明的则也测试不出来,对于是使用new声明的类型,它还可以检测出多层继承关系。
  2. <a name="mDmij"></a>
  3. ## Object.prototype.toString.call()
  4. 在任何值上调用 Object 原生的 toString() 方法,都会返回一个 `[object NativeConstructorName] `格式的字符串。每个类在内部都有一个` [[Class]]` 属性,这个属性中就指定了上述字符串中的构造函数名。<br />但是它**不能检测非原生构造函数的构造函数名**。
  5. ```javascript
  6. console.log(Object.prototype.toString.call(bool));//[object Boolean]
  7. console.log(Object.prototype.toString.call(num));//[object Number]
  8. console.log(Object.prototype.toString.call(str));//[object String]
  9. console.log(Object.prototype.toString.call(und));//[object Undefined]
  10. console.log(Object.prototype.toString.call(nul));//[object Null]
  11. console.log(Object.prototype.toString.call(arr));//[object Array]
  12. console.log(Object.prototype.toString.call(obj));//[object Object]
  13. console.log(Object.prototype.toString.call(fun));//[object Function]
  14. function Person(){}
  15. function Student(){}
  16. Student.prototype = new Person()
  17. var haoxl = new Student()
  18. console.log(Object.prototype.toString.call(haoxl));//[object Object]

constructor

指向该对象实例的__proto__.constructor
constructor不能判断undefined和null,并且使用它是不安全的,因为contructor的指向是可以改变的

  1. console.log(bool.constructor === Boolean);// true
  2. console.log(num.constructor === Number);// true
  3. console.log(str.constructor === String);// true
  4. console.log(arr.constructor === Array);// true
  5. console.log(obj.constructor === Object);// true
  6. console.log(fun.constructor === Function);// true
  7. console.log(haoxl.constructor === Student);// false
  8. console.log(haoxl.constructor === Person);// true

3、JavaScript prototype(原型对象)

所有的 JavaScript 对象都会从一个 prototype(原型对象)中继承属性和方法。
image.png
image.png

prototype 继承

所有的 JavaScript 对象都会从一个 prototype(原型对象)中继承属性和方法:

  • Date 对象从 Date.prototype 继承。
  • Array 对象从 Array.prototype 继承。
  • Person 对象从 Person.prototype 继承。

所有 JavaScript 中的对象都是位于原型链顶端的 Object 的实例。
JavaScript 对象有一个指向一个原型对象的链。当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。
Date 对象, Array 对象, 以及 Person 对象从 Object.prototype 继承。

添加属性和方法

有的时候我们想要在所有已经存在的对象添加新的属性或方法。
另外,有时候我们想要在对象的构造函数中添加属性或方法。
使用 prototype 属性就可以给对象的构造函数添加新的属性

  1. function Person(first, last, age, eyecolor) {
  2. this.firstName = first;
  3. this.lastName = last;
  4. this.age = age;
  5. this.eyeColor = eyecolor;
  6. }
  7. Person.prototype.nationality = "English";

使用 prototype 属性就可以给对象的构造函数添加新的方法

  1. function Person(first, last, age, eyecolor) {
  2. this.firstName = first;
  3. this.lastName = last;
  4. this.age = age;
  5. this.eyeColor = eyecolor;
  6. }
  7. Person.prototype.name = function() {
  8. return this.firstName + " " + this.lastName;
  9. };

4、类数组和数组的区别与转换

类数组对象,如 arguments 对象、NodeList 对象等,类数组对象有length属性,可以通过数组下标取值,但是类数组对象不能调用数组原型上的方法。
怎么把类数组对象转换成数组?

  1. Array.prototype.slice.call(NodeList, 0)
  2. Array.from(NodeList)
  3. var new = [...NodeList]

类数组和数组的区别与联系

csdn数组与类数组
数组用有丰富的内建方法供大家使用,而类数组是结构与数组十分相似(类数组相当于一个对象,key是数字,或者数字的字符串形式,并且拥有length属性)但是却没有数组那么丰富的内建方法,通常类数组可能还拥有一些别的属性。
image.png
image.png
作者:来个offer吧!!!!
链接:https://www.nowcoder.com/discuss/622767
来源:牛客网

5. bind,call,apply的区别

  1. 当我们使用一个函数需要改变this指向的时候才会用到callapplybind
  2. 如果你要传递的参数不多,则可以使用fn.call(thisObj, arg1, arg2 ...)
  3. 如果你要传递的参数很多,则可以用数组将参数整理好调用fn.apply(thisObj, [arg1, arg2 ...])
  4. 如果你想生成一个新的函数长期绑定某个函数给某个对象使用,则可以使用const newFn = fn.bind(thisObj); newFn(arg1, arg2...)
  5. call,apply,bind 不传参数自动绑定在 window

    6. new的原理

    new 大概会执行以下四个步骤:
  • 创建一个空对象
  • 将空对象的原型链连接到另一个对象
  • 执行构造函数中的代码并绑定 this 到这个对象
  • 如函数没有返回值,则返回该对象

    1. function _new() {
    2. // 参数为 对象A,属性
    3. // 1.创建一个空对象
    4. let obj = {}
    5. // 2.将该空对象的原型链连接到传入的对象
    6. let [Con, ...args] = arguments
    7. obj.__proto__ = Con.prototype
    8. // 3.执行函数并绑定 this
    9. let res = Con.apply(obj, args)
    10. // 4.如果函数有返回值并且为object,则返回函数的返回值,否则返回obj
    11. return res instanceof Object ? res : obj
    12. }
    13. function Person(name, age) {
    14. this.name = name
    15. this.age = age
    16. }
    17. Person.prototype.getName = function() {
    18. return this.name
    19. }
    20. let p = _new(Person, "sillywa", 23)

    7. 如何正确判断this

8. 闭包及其作用

一个函数有权访问另一个函数作用域中的变量,就形成闭包。
闭包可以用来隐藏变量,避免全局污染。也可以用于读取函数内部的变量。
缺点是:导致变量不会被垃圾回收机制回收,造成内存消耗。
image.png

变量生命周期

全局变量的作用域是全局性的,即在整个JavaScript程序中,全局变量处处都在。
而在函数内部声明的变量,只在函数内部起作用。这些变量是局部变量,作用域
是局部性的;函数的参数也是局部性的,只在函数内部起作用。
image.png
JavaScript 内嵌函数可以解决该问题。

JavaScript 内嵌函数

所有函数都能访问全局变量。
实际上,在 JavaScript 中,所有函数都能访问它们上一层的作用域。
JavaScript 支持嵌套函数。嵌套函数可以访问上一层的函数变量。
该实例中,内嵌函数 plus() 可以访问父函数的 counter 变量:

  1. function add() {
  2. var counter = 0;
  3. function plus() {counter += 1;}
  4. plus();
  5. return counter;
  6. }

如果我们能在外部访问 plus() 函数,这样就能解决计数器的困境。
我们同样需要确保 counter = 0 只执行一次。
我们需要闭包。

  1. var add = (function () {
  2. var counter = 0;
  3. return function () {return counter += 1;}
  4. })();
  5. add();
  6. add();
  7. add();
  8. // 计数器为 3

变量 add 指定了函数自我调用的返回字值。
自我调用函数只执行一次。设置计数器为 0。并返回函数表达式。
add变量可以作为一个函数使用。非常棒的部分是它可以访问函数上一层作用域的计数器。
这个叫作 JavaScript 闭包。它使得函数拥有私有变量变成可能
计数器受匿名函数的作用域保护,只能通过 add 方法修改。
闭包是一种保护私有变量的机制,在函数执行时形成私有的作用域,保护里面的私有变量不受外界干扰。
直观的说就是形成一个不销毁的栈环境

9. 原型和原型链

原型

①所有引用类型都有一个__proto__(隐式原型)属性,属性值是一个普通的对象
②所有函数都有一个prototype(原型)属性,属性值是一个普通的对象
③所有引用类型__proto__属性指向它构造函数的prototype

原型链

当访问一个对象的某个属性时,会先在这个对象本身属性上查找,如果没有找到,则会去它的__proto__隐式原型上查找,即它的构造函数的prototype,如果还没有找到就会再在构造函数的prototype的__proto__中查找,这样一层一层向上查找就会形成一个链式结构,我们称为原型链

  1. function Parent(month){
  2. this.month = month;
  3. }
  4. var child = new Parent('Ann');
  5. console.log(child.month); // Ann
  6. console.log(child.father); // undefined

image.png
访问链路为:
image.png :::warning ①一直往上层查找,直到到null还没有找到,则返回undefined
②Object.prototype.proto === null
③所有从原型或更高级原型中的得到、执行的方法,其中的this在执行时,指向当前这个触发事件执行的对象 :::

无论什么时候,只要创建了一个函数,就会根据为该函数创建一个 prototype 属性,这个属性指向函数的原型对象。在默认情况下,所有原型对象都会获得一个 constructor,该属性是一个指向 prototype 属性所在函数的指针。
原型链规定了对象如何查找属性,对于一个对象来说,如果它本身没有某个属性,则会沿着原型链一直向上查找,知道找到属性或者查找完整个原型链。
原型链是实现继承的主要方法,其基本思想是利用原型链让一个引用类型继承另一个引用类型的属性和方法。

10. 继承的实现方式及比较(未完)

1. 简单的原型继承

  1. function SuperType() {
  2. this.name = "super"
  3. }
  4. function SubType() {}
  5. // 利用原型链实现继承
  6. SubType.prototype = new SuperType()
  7. var instance1 = new SubType()
  8. console.log(instance1.name) // super

简单的原型继承存在以下两个问题:

  • 包含引用类型值的原型属性会被所有实例共享,在通过原型来实现继承时,原型实际上也会变成另一个类型的实例。于是,原先的实例属性也就变成了现在的原型属性。思考一下代码: ```javascript function SuperType() { this.names = [“sillywa”, “xinda”] } function SubType() {}

// 利用原型链实现继承 SubType.prototype = new SuperType()

var instance1 = new SubType() instance1.names.push(“hahah”) console.log(instance1.names) // [“sillywa”, “xinda”, “hahah”]

var instance2 = new SubType() console.log(instance2.names) // [“sillywa”, “xinda”, “hahah”]

  1. <a name="PPF2l"></a>
  2. # 11. 对象的深拷贝与浅拷贝
  3. <a name="lnLDK"></a>
  4. ## 1. 浅拷贝的实现方法
  5. - 遍历赋值
  6. <a name="stbfr"></a>
  7. ### `for in`
  8. `var key in obj`
  9. ```javascript
  10. function clone(obj) {
  11. var cloneObj = {}
  12. // for in 遍历,会遍历原型链里面的属性,所以需要排除原型链
  13. for(var key in obj) {
  14. if(obj.hasOwnProperty(key)) {
  15. cloneObj[key] = obj[key]
  16. }
  17. }
  18. return cloneObj
  19. }


Object.keys()

var key of Object.keys(obj)

  1. function clone(obj) {
  2. var cloneObj = {}
  3. // Object.keys()不会遍历到原型链中的属性
  4. for(var key of Object.keys(obj)) {
  5. cloneObj[key] = obj[key]
  6. }
  7. return cloneObj
  8. }

Object.entries()

var [key, value] of Object.entries(obj)

  1. function clone(obj) {
  2. var cloneObj = {}
  3. for(var [key, value] of Object.entries(obj)) {
  4. cloneObj[key] = value
  5. }
  6. return cloneObj
  7. }

``Object.assign()

  1. function clone(obj) {
  2. return Object.assign(obj, {})
  3. }

2. 深拷贝的实现方法

JSON.stringfy()JSON.parse()

  1. function deepClone(obj) {
  2. return JSON.parse(JSON.stringify(obj))
  3. }

存在问题:遇到函数,undefined,Symbol,Date对象时会自动忽略,遇到正则时会返回空对象

递归

  1. // 三种遍历方式就会有三种递归
  2. // for in
  3. function deepClone(obj) {
  4. if(!obj || typeof obj != "object") return obj
  5. if(obj instanceof RegExp) return new RegExp(obj)
  6. var cloneObj = new obj.constructor
  7. for(var key in obj) {
  8. if(obj.hasOwnProperty(key)){
  9. cloneObj[key] = deepClone(obj[key])
  10. }
  11. }
  12. return cloneObj
  13. }
  14. // Object.keys()
  15. function deepClone(obj) {
  16. if(!obj || typeof obj != "object") return obj
  17. if(obj instanceof RegExp) return new RegExp(obj)
  18. var cloneObj = new obj.constructor
  19. for(var key of Object.keys(obj)) {
  20. cloneObj[key] = deepClone(obj[key])
  21. }
  22. return cloneObj
  23. }
  24. // Object.entries()
  25. function deepClone(obj) {
  26. if(!obj || typeof obj != "object") return obj
  27. if(obj instanceof RegExp) return new RegExp(obj)
  28. var cloneObj = new obj.constructor
  29. for(var [key, value] of Object.entries(obj)) {
  30. cloneObj[key] = deepClone(value)
  31. }
  32. return cloneObj
  33. }