菜鸡打卡
1.复杂数据类型如何转变为字符串
- 首先,会调用 valueOf 方法,如果方法的返回值是一个基本数据类型,就返回这个值,如果调用 valueOf 方法之后的放回置仍旧是一个复杂数据类型,就会调用该对象的 toString 方法,如果 toString 方法调用之后的返回值是一个基本数据类型,就会返回这个值,如果 toString 方法调用之后的返回值是一个复杂数据类型,就报一个错误。
//1
const obj = {
valueOf: function () {
return 1
}
}
console.log(obj + '') // '1'
//2
const obj2 = {
valueOf: function () {
return [1, 2]
}
}
console.log(obj2 + '')//[object Object]
//3
const obj3 = {
valueOf: function () {
return [1, 2]
},
toString: function () {
return 1
}
}
console.log(obj3 + '') //'1'
//4
const obj4 = {
valueOf: function () {
return [1, 2]
},
toString: function () {
return [1, 2, 3]
}
}
console.log(obj4 + '')// 报错 Uncaught TypeError: Cannot convert object to primitive value
扩展
let arr = [new Object(), new Date(), new RegExp(), new String(), new Number(), new Boolean(),
new Function(), new Array(), Math]
console.log(arr.length); // 9
for (let i=0; i<arr.length; i++) {
arr[i].valueOf = function () {
return [1, 2, 3]
}
arr[i].toString = function () {
return 'toString'
}
console.log(arr[i] + '')
}
1.若 return[1,2,3]处为 return "valueof",得到的返回值是valueof toString 7valueof 说明:其他八种复杂类型是调用valueOf方法,时间对象调用toString方法。
2.改成return[1,2,3],得到的返回值是 9toString 说明:执行 valueof 后都来执行 toString。
2.javascript 的 typeof 返回哪些数据类型
7 种分别为 string、boolean、number、Object、Function、undefined、symbol(ES6),bigInt(不考虑这个,没用过哈哈)
//number
typeof (10);
typeof (NaN); // NaN在JavaScript中代表的是特殊非数字值,它本身是一个数字类型。
typeof (Infinity)
//boolean
typeof(true);
typeof(false);
//string
typeof("abc");
//undefined
typeof(undefined);
typeof(a); // 不存在的变量
//object
// 对象,数组,null返回object
typeof(null);
typeof(window);
//function
typeof(Array);
typeof(Date);
//symbol
typeof Symbol() // ES6提供的新的类型
3.一次 js 请求一般情况下有哪些地方会有缓存处理?
- DNS 缓存
- CDN 缓存
- 浏览器缓存
- 服务器缓存
DNS 缓存
DNS 缓存指 DNS 返回了正确的 IP 之后,系统就会将这个结果临时储存起来。并且它会为缓存设定一个失效时间 (例如 N 小时),在这 N 小时之内,当你再次访问这个网站时,系统就会直接从你电脑本地的 DNS 缓存中把结果交还给你,而不必再去询问 DNS 服务器,变相“加速”了网址的解析。当然,在超过 N 小时之后,系统会自动再次去询问DNS 服务器获得新的结果。 所以,当你修改了 DNS 服务器,并且不希望电脑继续使用之前的 DNS 缓存时,就需要手动去清除本地的缓存了。
本地 DNS 迟迟不生效或者本地 dns 异常等问题,都会导致访问某些网站出现无法访问的情况,这个时候我们就 需要手动清除本地 dns 缓存,而不是等待!
CDN 缓存
和 Http 类似,客户端请求数据时,先从本地缓存查找,如果被请求数据没有过期,拿过来用,如果过期,就向 CDN 边缘节点发起请求。CDN 便会检测被请求的数据是否过期,如果没有过期,就返回数据给客户端,如果过期,CDN 再向源站发送请求获取新数据。和买家买货,卖家没货,卖家再进货一个道理^-^。
CDN 边缘节点缓存机制,一般都遵守 http 标准协议,通过 http 响应头中的 Cache-Control 和 max-age 的字 段来设置
CDN 边缘节点的数据缓存时间。
浏览器缓存
浏览器缓存(Browser Caching)是为了节约网络的资源加速浏览,浏览器在用户磁盘上对 近请求过的文档进行存储,当访问者再次请求这个页面时,浏览器就可以从本地磁盘显示文档,这样就可以加速页面的阅览。
浏览器缓存主要有两类:缓存协商:Last-modified ,Etag 和彻底缓存:cache-control,Expires。浏览器都有对应清除缓存的方法。
服务器缓存
服务器缓存有助于优化性能和节省宽带,它将需要频繁访问的 Web 页面和对象保存在离用户更近的系统中,当再次访问这些对象的时候加快了速度。
4.你对闭包的理解?优缺点?
概念:闭包就是能够读取其他函数内部变量的函数。
三大特新:
- 函数嵌套函数
- 函数内部可以引用外部的参数和变量
- 参数和变量不会被垃圾回收机制回收
优点
- 希望一个变量长期储存在内存中
- 避免内部可以引用外部的参数和变量
- 私有成员的存在
缺点
- 常驻内存,增加内存中使用量
- 使用不当会很容易造成内存泄漏
function outer() {
let name = 'mint';
function inner() {
console.log(name);
}
return inner
}
outer()();// mint
function sayHi(name) {
return () => {
console.log(`Hi! ${name}`);
}
}
const test = sayHi("xiaoming");
test() // Hi! xiaoming
虽然sayHi函数以及执行完毕,但是起获得对象也不会被销毁,因为 test 函数仍然引用这 sayHi 函数中变量 name, 这就是闭包。但是因为闭包引用着另一个函数的变量,导致另一个函数已经不使用也无法销毁,所以闭包使用过,会占用较多的内存,这也是一个副作用。
解析:
由于在 ECMA2015 中,只有函数才能分割作用域,函数内部可以访问当前作用域的变量,但是外部无法访问函数内部的变量,所以闭包可以理解成“定义在一个函数内部的函数,外部可以通过内部返回的函数访问内部函数的变量“。在本质上,闭包是将函数内部和函数外部连接起来的桥梁。
5.new 一个对象的过程中发生了什么
new 一个对象的四个过程:
function Person(name) {
this.name = name;
}
let person = new Person("mint");
// 1.创建空对象
let obj = {}
//2.设置原型链:设置新对象的 constructor 属性为构造函数的名称,设置新对象的__proto__属性执行构造函数的 prototype 对象
obj.constructor = Person();
obj.__proto__ = Person.prototype;
// 3.改变this指向:使用新对象调用函数在的 this 指向新石烈对象 obj
let result = Person.call(obj)//{}构造函数()
//4.返回值:如果无返回值或一个非对象值,则将新对象返回;如果返回值是一个新对象的话,那么直接返回该对象
if (typeof(result) == "object") {
person = result
} else {
person = obj
}
6.for in 和 for of 的区别
for in
-
一般用于遍历对象的可枚举属性。以及对象从构造函数原型中继承的属性。对于每个不同的属性,语句都会被执行
-
不建议使用 for in 遍历数组,因为输出的顺序是不固定的
-
如果迭代的对象的变量值是 null 或者 undefined, for in 不执行循环体,建议在使用 for in 循环之前,先检查该对象的值是不是 null 或者 undefined
*for of *
- for…of 语句在可迭代对象(包括 Array,Map,Set,String,TypedArray,arguments 对象等等)上创建一个迭代循环,调用自定义迭代钩子,并为每个不同属性的值执行语句
let s = {
a: 1,
b: 2,
c: 3
}
let s1 = Object.create(s)
for (let prop in s1) {
console.log(prop);//a b c
console.log(s1[prop]); // 1 2 3
}
for (let prop of s1) {
console.log(prop) //报错如下 Uncaught TypeError: s1 is not iterable
}
for (let prop of Object.keys(s1)) {
console.log(prop); // a b c
console.log(s1[prop]); //1 2 3
}
7.for in、Object. keys 和 Object. getOwnPropertyNames 对属性遍历有什么区别?
- for in 会遍历自身及原型链上的可枚举属性
- Object. keys 会将对象自身的可枚举属性的 key 输出
- Object. getOwnPropertyNames 会将自身所有的属性的 key 输出
解析:
ECMAScript 将对象的属性分为两种:数据属性和访问器属性。
// parent继承自Object.prototype,有一个可枚举的属性a(enumerable:true)。
let parent = Object.create(Object.prototype, {
a: {
value: 123,
writable: true,
enumerable: true,
configurable: true
}
})
//child 继承自 parent ,b可枚举,c不可枚举
let child = Object.create(parent, {
b: {
value: 2,
writable: true,
enumerable: true,
configurable: true
},
c: {
value: 3,
writable: true,
enumerable: false,
configurable: true
}
})
for in
for (let key in child) {
console.log(key);
}
//b
//a
//for in 会遍历自身及原型链上的可枚举属性
如果只想输出自身的可枚举属性,可使用 hasOwnProperty 进行判断(数组与对象都可以,此处用数组做例子)
let arr = [1, 2, 3];
Array.prototype.xxx = 1231235;
for (let i in arr) {
if (arr.hasOwnProperty(i)) {
console.log(arr[i]);
}
}
// 1
// 2
// 3
Object.keys
console.log(Object.keys(child));
// [ 'b' ]
// Object.keys 会将对象自身的可枚举属性的key输出
Object. getOwnPropertyNames
console.log(Object.getOwnPropertyNames(child));
//[ 'b', 'c' ]
// 会将自身所有的属性的key输出
8.Object. prototype. toString. call()和 instanceOf 和 Array. isArray() 区别好坏
Object. prototype. toString. call()
- 优点:这种方法对于所有基本的数据类型都能进行判断,即使是 null 和 undefined 。
- 缺点:不能精准判断自定义对象,对于自定义对象只会返回[object Object]
instanceOf
- 优点:instanceof 可以弥补 Object. prototype. toString. call()不能判断自定义实例化对象的缺点。 缺点: instanceof 只能用来判断对象类型,原始类型不可以。并且所有对象类型 instanceof Object 都是 true,且不同于其他两种方法的是它不能检测出 iframes。
Array. isArray()
-
优点:当检测 Array 实例时,Array. isArray 优于 instanceof ,因为 Array. isArray 可以检测出
-
iframes 缺点:只能判别数组
解析
Object. prototype. toString. call()
每一个继承 Object 的对象都有 toString 方法,如果 toString 方法没有重写的话,会返回 [Object type],其中type 为对象的类型。但当除了 Object 类型的对象外,其他类型直接使 toString 方法时,会直接返回都是内容的字符串,所以我们需要使用 call 或者 apply 方法来改变 toString 方法的执行上下文。
const an = ['Hello', 'An']
console.log(an.toString());// "Hello ,An"
console.log(Object.prototype.toString.call(an));// '[object Array]'
这种方法对于所有基本的数据类型都能进行判断,即使是 null 和 undefined 。
Object.prototype.toString.call("An"); // "[object String]"
Object.prototype.toString.call(1); // "[object Number]"
Object.prototype.toString.call(Symbol(1)); // "[object Symbol]"
Object.prototype.toString.call(null); // "[object Null]"
Object.prototype.toString.call(undefined); // "[object Undefined]"
Object.prototype.toString.call(function() {}); // "[object Function]"
Object.prototype.toString.call({
name: "An"
}); // "[object Object]"
缺点:不能精准判断自定义对象,对于自定义对象只会返回[object Object]
function f(name) {
this.name = name;
}
var f1 = new f("martin");
console.log(Object.prototype.toString.call(f1)); //[object Object]
Object.prototype.toString.call(); // 常用于判断浏览器内置对象。
instanceof
instanceof 的内部机制是通过判断对象的原型链中是不是能找到类型的 prototype。
使用 instanceof 判断一个对象是否为数组,instanceof 会判断这个对象的原型链上是否会找到对应的 Array 的 原型,找到返回 true,否则返回 false。
[] instanceof Array; // true
但 instanceof 只能用来判断对象类型,原始类型不可以。并且所有对象类型 instanceof Object 都是 true。
[] instanceof Object; // true
优点:instanceof 可以弥补 Object. prototype. toString. call()不能判断自定义实例化对象的缺点。
缺点:instanceof 只能用来判断对象类型,原始类型不可以。并且所有对象类型 instanceof Object 都是 true,且不同于其他两种方法的是它不能检测出 iframes。
function f(name) {
this.name = name;
}
var f1 = new f("martin");
console.log(f1 instanceof f); //true
Array. isArray()
- 功能:用来判断对象是否为数组
- instanceof 与 isArray
当检测 Array 实例时,Array. isArray 优于 instanceof ,因为 Array. isArray 可以检测出 iframes
var iframe = document.createElement("iframe");
document.body.appendChild(iframe);
xArray = window.frames[ window.frames.length ‐ 1].Array;
var arr = new xArray(1, 2, 3); // [1,2,3]
// Correctly checking for Array
Array.isArray(arr); // true
Object.prototype.toString.call(arr); // true
// Considered harmful, because doesn't work though iframes
arr instanceof Array; // false
缺点:只能判别数组
Array. isArray() 与 Object. prototype. toString. call().
Array. isArray()是 ES5 新增的方法,当不存在Array.isArray() ,可以用 Object. prototype. toString. call() 实现。
if (!Array.isArray) {
Array.isArray = function(arg) {
return Object.prototype.toString.call(arg) === "[object Array]";
};
}
9.JS 单线程还是多线程,如何显示异步操作
JS 本身是单线程的,他是依靠浏览器完成的异步操作。
具体步骤:
-
主线程 执行 js 中所有的代码。
-
主线程 在执行过程中发现了需要异步的任务,任务后扔给浏览器(浏览器创建多个线程执行),并在 callback queue 中创建对应的回调函数(回调函数是一个对象,包含该函数是否执行完等)。
-
主线程 已经执行完毕所有同步代码。开始监听 callback queue 一旦 浏览器 中某个线程任务完成将会改变回调函数的状态。主线程查看到某个函数的状态为已完成,就会执行该函数。
10.JavaScript 数组的函数 map/forEach/reduce/filter
map
//作用:对数组进行遍历
//返回值:新的数组
// 是否改变:否
let arr = [2, 5, 3, 4]
let res = arr.map(function (value) {
return value + 1
})
console.log(res);//[ 3, 6, 4, 5 ]
console.log(arr);//[ 2, 5, 3, 4 ]
forEach
// 作用:遍历数组的每一项
// 返回值:undefined
// 是否改变:否
let arr = [2, 5, 3, 4];
let res = arr.forEach(function (value) {
console.log(value);
})
console.log(res); //undefined
console.log(arr); //[2,5,3,4]
reduce
// 作用:对数组进行迭代,然后两两进行操作,后返回一个值
// 返回值:return出来的结果
// 是否改变:不会
var arr = [1, 2, 3, 4];
var res = arr.reduce(function(a, b) {
return a * b;
});
console.log(res); // 24
console.log(arr); // [1, 2, 3, 4]
filter
// 作用: 筛选一部分元素
// 返回值: 一个满足筛选条件的新数组
// 是否改变原有数组:不会
var arr = [2, 5, 3, 4];
var res = arr.filter(function(value) {
return value > 3;
});
console.log(res); //[5,4]
console.log(arr); //[2,5,3,4]