1. 原始值和引用值类型及区别
原始值,也叫基本类型:如null,undefined,string,number,boolean
引用值,如Object,Function,Date,Array,RegExp
原始值与引用值的区别:
- 原始值存储在栈中,引用值存储在堆中
- 原始值是以值的拷贝方式进行赋值,值是不可变的;
- 引用值是以引用的拷贝方式进行赋值,值是可变的
原始值的比较是值的比较,引用值的比较是引用的比较(比较引用的是否为同一对象)
2.判断数据类型的常用方法
基本数据类型(Undefined、Null、Boolean、Number、String)
- 复杂数据类型 (Object)
var bool = truevar num = 1var str = 'abc'var und = undefinedvar nul = nullvar arr = [1,2,3]var obj = {name:'haoxl',age:18}var fun = function(){console.log('I am a function')}
typeof
typeof 进行类型判断的返回值有: ‘undeinfed’,’string’,’number’,’boolean’,’object’,’symbol’,’function’
typeof 对 null 返回 ‘object’,对正则,数组返回 ‘object’console.log(typeof bool); //booleanconsole.log(typeof num);//numberconsole.log(typeof str);//stringconsole.log(typeof und);//undefinedconsole.log(typeof nul);//objectconsole.log(typeof arr);//objectconsole.log(typeof obj);//objectconsole.log(typeof fun);//function
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
从结果中看出instanceof不能区别undefined和null,而且对于基本类型如果不是用new声明的则也测试不出来,对于是使用new声明的类型,它还可以检测出多层继承关系。<a name="mDmij"></a>## Object.prototype.toString.call()在任何值上调用 Object 原生的 toString() 方法,都会返回一个 `[object NativeConstructorName] `格式的字符串。每个类在内部都有一个` [[Class]]` 属性,这个属性中就指定了上述字符串中的构造函数名。<br />但是它**不能检测非原生构造函数的构造函数名**。```javascriptconsole.log(Object.prototype.toString.call(bool));//[object Boolean]console.log(Object.prototype.toString.call(num));//[object Number]console.log(Object.prototype.toString.call(str));//[object String]console.log(Object.prototype.toString.call(und));//[object Undefined]console.log(Object.prototype.toString.call(nul));//[object Null]console.log(Object.prototype.toString.call(arr));//[object Array]console.log(Object.prototype.toString.call(obj));//[object Object]console.log(Object.prototype.toString.call(fun));//[object Function]function Person(){}function Student(){}Student.prototype = new Person()var haoxl = new Student()console.log(Object.prototype.toString.call(haoxl));//[object Object]
constructor
指向该对象实例的__proto__.constructor。
constructor不能判断undefined和null,并且使用它是不安全的,因为contructor的指向是可以改变的
console.log(bool.constructor === Boolean);// trueconsole.log(num.constructor === Number);// trueconsole.log(str.constructor === String);// trueconsole.log(arr.constructor === Array);// trueconsole.log(obj.constructor === Object);// trueconsole.log(fun.constructor === Function);// trueconsole.log(haoxl.constructor === Student);// falseconsole.log(haoxl.constructor === Person);// true
3、JavaScript prototype(原型对象)
所有的 JavaScript 对象都会从一个 prototype(原型对象)中继承属性和方法。
prototype 继承
所有的 JavaScript 对象都会从一个 prototype(原型对象)中继承属性和方法:
- Date 对象从
Date.prototype继承。 - Array 对象从
Array.prototype继承。 - Person 对象从
Person.prototype继承。
所有 JavaScript 中的对象都是位于原型链顶端的 Object 的实例。
JavaScript 对象有一个指向一个原型对象的链。当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。
Date 对象, Array 对象, 以及 Person 对象从 Object.prototype 继承。
添加属性和方法
有的时候我们想要在所有已经存在的对象添加新的属性或方法。
另外,有时候我们想要在对象的构造函数中添加属性或方法。
使用 prototype 属性就可以给对象的构造函数添加新的属性:
function Person(first, last, age, eyecolor) {this.firstName = first;this.lastName = last;this.age = age;this.eyeColor = eyecolor;}Person.prototype.nationality = "English";
使用 prototype 属性就可以给对象的构造函数添加新的方法:
function Person(first, last, age, eyecolor) {this.firstName = first;this.lastName = last;this.age = age;this.eyeColor = eyecolor;}Person.prototype.name = function() {return this.firstName + " " + this.lastName;};
4、类数组和数组的区别与转换
类数组对象,如 arguments 对象、NodeList 对象等,类数组对象有length属性,可以通过数组下标取值,但是类数组对象不能调用数组原型上的方法。
怎么把类数组对象转换成数组?
Array.prototype.slice.call(NodeList, 0)Array.from(NodeList)var new = [...NodeList]
类数组和数组的区别与联系
csdn数组与类数组
数组用有丰富的内建方法供大家使用,而类数组是结构与数组十分相似(类数组相当于一个对象,key是数字,或者数字的字符串形式,并且拥有length属性)但是却没有数组那么丰富的内建方法,通常类数组可能还拥有一些别的属性。

作者:来个offer吧!!!!
链接:https://www.nowcoder.com/discuss/622767
来源:牛客网
5. bind,call,apply的区别
- 当我们使用一个函数需要改变this指向的时候才会用到call
applybind - 如果你要传递的参数不多,则可以使用
fn.call(thisObj, arg1, arg2 ...) - 如果你要传递的参数很多,则可以用数组将参数整理好调用
fn.apply(thisObj, [arg1, arg2 ...]) - 如果你想生成一个新的函数长期绑定某个函数给某个对象使用,则可以使用
const newFn = fn.bind(thisObj); newFn(arg1, arg2...) - call,apply,bind 不传参数自动绑定在 window
6. new的原理
new 大概会执行以下四个步骤:
- 创建一个空对象
- 将空对象的原型链连接到另一个对象
- 执行构造函数中的代码并绑定 this 到这个对象
如函数没有返回值,则返回该对象
function _new() {// 参数为 对象A,属性// 1.创建一个空对象let obj = {}// 2.将该空对象的原型链连接到传入的对象let [Con, ...args] = argumentsobj.__proto__ = Con.prototype// 3.执行函数并绑定 thislet res = Con.apply(obj, args)// 4.如果函数有返回值并且为object,则返回函数的返回值,否则返回objreturn res instanceof Object ? res : obj}function Person(name, age) {this.name = namethis.age = age}Person.prototype.getName = function() {return this.name}let p = _new(Person, "sillywa", 23)
7. 如何正确判断this
8. 闭包及其作用
一个函数有权访问另一个函数作用域中的变量,就形成闭包。
闭包可以用来隐藏变量,避免全局污染。也可以用于读取函数内部的变量。
缺点是:导致变量不会被垃圾回收机制回收,造成内存消耗。
变量生命周期
全局变量的作用域是全局性的,即在整个JavaScript程序中,全局变量处处都在。
而在函数内部声明的变量,只在函数内部起作用。这些变量是局部变量,作用域
是局部性的;函数的参数也是局部性的,只在函数内部起作用。
JavaScript 内嵌函数可以解决该问题。
JavaScript 内嵌函数
所有函数都能访问全局变量。
实际上,在 JavaScript 中,所有函数都能访问它们上一层的作用域。
JavaScript 支持嵌套函数。嵌套函数可以访问上一层的函数变量。
该实例中,内嵌函数 plus() 可以访问父函数的 counter 变量:
function add() {var counter = 0;function plus() {counter += 1;}plus();return counter;}
如果我们能在外部访问 plus() 函数,这样就能解决计数器的困境。
我们同样需要确保 counter = 0 只执行一次。
我们需要闭包。
var add = (function () {var counter = 0;return function () {return counter += 1;}})();add();add();add();// 计数器为 3
变量 add 指定了函数自我调用的返回字值。
自我调用函数只执行一次。设置计数器为 0。并返回函数表达式。
add变量可以作为一个函数使用。非常棒的部分是它可以访问函数上一层作用域的计数器。
这个叫作 JavaScript 闭包。它使得函数拥有私有变量变成可能。
计数器受匿名函数的作用域保护,只能通过 add 方法修改。
闭包是一种保护私有变量的机制,在函数执行时形成私有的作用域,保护里面的私有变量不受外界干扰。
直观的说就是形成一个不销毁的栈环境。
9. 原型和原型链
原型
①所有引用类型都有一个__proto__(隐式原型)属性,属性值是一个普通的对象
②所有函数都有一个prototype(原型)属性,属性值是一个普通的对象
③所有引用类型的__proto__属性指向它构造函数的prototype
原型链
当访问一个对象的某个属性时,会先在这个对象本身属性上查找,如果没有找到,则会去它的__proto__隐式原型上查找,即它的构造函数的prototype,如果还没有找到就会再在构造函数的prototype的__proto__中查找,这样一层一层向上查找就会形成一个链式结构,我们称为原型链。
function Parent(month){this.month = month;}var child = new Parent('Ann');console.log(child.month); // Annconsole.log(child.father); // undefined

访问链路为:
:::warning
①一直往上层查找,直到到null还没有找到,则返回undefined
②Object.prototype.proto === null
③所有从原型或更高级原型中的得到、执行的方法,其中的this在执行时,指向当前这个触发事件执行的对象
:::
无论什么时候,只要创建了一个函数,就会根据为该函数创建一个 prototype 属性,这个属性指向函数的原型对象。在默认情况下,所有原型对象都会获得一个 constructor,该属性是一个指向 prototype 属性所在函数的指针。
原型链规定了对象如何查找属性,对于一个对象来说,如果它本身没有某个属性,则会沿着原型链一直向上查找,知道找到属性或者查找完整个原型链。
原型链是实现继承的主要方法,其基本思想是利用原型链让一个引用类型继承另一个引用类型的属性和方法。
10. 继承的实现方式及比较(未完)
1. 简单的原型继承
function SuperType() {this.name = "super"}function SubType() {}// 利用原型链实现继承SubType.prototype = new SuperType()var instance1 = new SubType()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”]
<a name="PPF2l"></a># 11. 对象的深拷贝与浅拷贝<a name="lnLDK"></a>## 1. 浅拷贝的实现方法- 遍历赋值<a name="stbfr"></a>### `for in``var key in obj````javascriptfunction clone(obj) {var cloneObj = {}// for in 遍历,会遍历原型链里面的属性,所以需要排除原型链for(var key in obj) {if(obj.hasOwnProperty(key)) {cloneObj[key] = obj[key]}}return cloneObj}
Object.keys()
var key of Object.keys(obj)
function clone(obj) {var cloneObj = {}// Object.keys()不会遍历到原型链中的属性for(var key of Object.keys(obj)) {cloneObj[key] = obj[key]}return cloneObj}
Object.entries()
var [key, value] of Object.entries(obj)
function clone(obj) {var cloneObj = {}for(var [key, value] of Object.entries(obj)) {cloneObj[key] = value}return cloneObj}
``Object.assign()
function clone(obj) {return Object.assign(obj, {})}
2. 深拷贝的实现方法
JSON.stringfy()与 JSON.parse()
function deepClone(obj) {return JSON.parse(JSON.stringify(obj))}
存在问题:遇到函数,undefined,Symbol,Date对象时会自动忽略,遇到正则时会返回空对象
递归
// 三种遍历方式就会有三种递归// for infunction deepClone(obj) {if(!obj || typeof obj != "object") return objif(obj instanceof RegExp) return new RegExp(obj)var cloneObj = new obj.constructorfor(var key in obj) {if(obj.hasOwnProperty(key)){cloneObj[key] = deepClone(obj[key])}}return cloneObj}// Object.keys()function deepClone(obj) {if(!obj || typeof obj != "object") return objif(obj instanceof RegExp) return new RegExp(obj)var cloneObj = new obj.constructorfor(var key of Object.keys(obj)) {cloneObj[key] = deepClone(obj[key])}return cloneObj}// Object.entries()function deepClone(obj) {if(!obj || typeof obj != "object") return objif(obj instanceof RegExp) return new RegExp(obj)var cloneObj = new obj.constructorfor(var [key, value] of Object.entries(obj)) {cloneObj[key] = deepClone(value)}return cloneObj}
