== 与 ===

对于 == 来说,如果对比双方的类型不一样的话,就会进行类型转换,就会进行如下判断流程:

1.首先会判断两者类型是否相同,相同则会进行严格相等比较===

2.判断是否在对比nullundefined,是的话就会返回true

3.判断两者类型是否为stringnumber,是的话就会将字符串转换为number

4.判断两者类型是否有boolean,有的话将boolean转换为number

5.判断是否一方为object,另一方为stringnumbersymbol,是的话,将object转换为原始类型再进行判断

思考题:

[] == ![], 为什么?

根据上面判断流程, ![]false(上一节有涉及),然后进入4,转换为[] == 0,然后进入5,转换为'' == 0,进入3,转换为0 == 0,成立。

更多对比,查看

深浅拷贝

浅拷贝

1.使用Object.assign可以实现,它会拷贝所有的属性值到新的对象中,如果属性值是对象的话,拷贝的是地址。

2.通过展开运算符...来实现浅拷贝

深拷贝

一般可以通过JSON.parse(JSON.stringify(object))来解决。

let a = {
age: 1,
jobs: {
first: 'FE'
}
}
let b = JSON.parse(JSON.stringify(a))
a.jobs.first = 'native'
console.log(b.jobs.first) // FE

但是该方法有局限性的:

  • 会忽略undefined
  • 会忽略symbol
  • 不能序列化函数
  • 不能解决循环引用的对象

如果你所需拷贝的对象含有内置类型并且不包含函数,可以使用MessageChannel

function structuralClone(obj) {
return new Promise(resolve => {
const { port1, port2 } = new MessageChannel()
port2.onmessage = ev => resolve(ev.data)
port1.postMessage(obj)
})
} var obj = {
a: 1,
b: {
c: 2
}
} obj.b.d = obj.b // 注意该方法是异步的
// 可以处理 undefined 和循环引用对象
const test = async () => {
const clone = await structuralClone(obj)
console.log(clone)
}
test()

也可以自己实现一个深拷贝,但是其实实现一个深拷贝是很困难的,需要我们考虑好多种边界情况,比如原型链如何处理、DOM 如何处理等等,下面实现的深拷贝只是简易版,更推荐使用lodash深拷贝

function deepClone(obj) {
function isObject(o) {
return (typeof o === 'object' || typeof o === 'function') && o !== null
} if (!isObject(obj)) {
throw new Error('非对象')
} let isArray = Array.isArray(obj)
let newObj = isArray ? [...obj] : { ...obj }
Reflect.ownKeys(newObj).forEach(key => {
newObj[key] = isObject(obj[key]) ? deepClone(obj[key]) : obj[key]
}) return newObj
} let obj = {
a: [1, 2, 3],
b: {
c: 2,
d: 3
}
}
let newObj = deepClone(obj)
newObj.b.c = 1
console.log(obj.b.c) // 2

原型

函数对象都有prototype, 任何一个对象都有原型,我们可以通过非标准属性__proto__来访问一个对象的原型

// 纯对象的原型默认是个空对象
console.log({}.__proto__); // => {} function Student(name, grade) {
this.name = name;
this.grade = grade;
} const stu = new Student('xiaoMing', 6);
// Student 类型实例的原型,默认也是一个空对象
console.log(stu.__proto__); // => Student {}

理解几个名词:

  • constructor: 构造器,我们也可以称之为类,我们可以通过new构造器来构造一个实例。

  • __proto__: 一个访问器属性,非标准,暴露了通过它访问的对象的内部[[Prototype]](一个对象或null),不过更推荐使用ES6新增的Reflect.getPrototypeOf或者Object.getPrototypeOf()方法。

  • prototype: 原型对象,每个函数对象都有prototype

它们之间的关系如下图

原型链

当在一个对象obj上访问某个属性时,如果不存在于obj,那么便会去对象的原型也就是obj.__proto__上去找这个属性。如果有则返回这个属性,没有则去对象obj的原型的原型也就是obj.__proto__.__proto__去找,重复以上步骤。一直到访问纯对象的原型也就是Object.prototype,没有的话续往上找也就是Object.prototype.__proto__,其实就是null,直接返回undefined

举个例子

function Student(name, grade) {
this.name = name;
this.grade = grade;
} const stu = new Student();
console.log(stu.notExists); // => undefined

访问stu.notExists的整个过程是:

1.先看stu上是否存在notExists,不存在,所以看stu.__proto__

stu.__proto__ === Student.prototype // => true

2.stu.__proto__上也不存在notExists属性,再看stu.__proto__.__proto__,其实就是纯对象的原型:Object.prototype

console.log(stu.__proto__.__proto__ === {}.__proto__); // => true

3.纯对象的原型上也不存在notExists属性,再往上,到stu.__proto__.__proto__.__proto__上去找,其实就是null

console.log(new Object().__proto__.__proto__); // => null

4.null不存在notExists属性,返回undefined

各个原型之间构成的链,我们称之为原型链。

函数Student的原型链:

思考题

function Page() {
return this.hosts;
}
Page.hosts = ['h1'];
Page.prototype.hosts = ['h2']; const p1 = new Page();
const p2 = Page(); console.log(p1.hosts);
console.log(p2.hosts);

运行结果是:先输出undefiend,然后报错TypeError: Cannot read property 'hosts' of undefined

为什么console.log(p1.hosts)是输出undefiend呢,因为new的时候如果return了对象,会直接拿这个对象作为new的结果,因此,p1应该是this.hosts的结果,

而在new Page()的时候,this是一个以Page.prototype为原型的target对象,所以这里this.hosts可以访问到Page.prototype.hosts也就是['h2']

这样 p1 就是等于['h2']['h2']没有hosts属性所以返回undefined

为什么console.log(p2.hosts)会报错呢,p2是直接调用Page构造函数的结果,直接调用page函数,这个时候this指向全局对象,全局对象并没hosts属性,因此返回undefined,往undefined上访问hosts当然报错。

JS基础知识点(二)的更多相关文章

  1. 进击Node.js基础(二)

    一.一个牛逼闪闪的知识点Promise npm install bluebird 二.Promise实例 ball.html <!doctype> <!DOCTYPE html> ...

  2. js基础知识点收集

    js基础知识点收集 js常用基本类型 function show(x) { console.log(typeof(x)); // undefined console.log(typeof(10)); ...

  3. js基础练习二之简易日历

    今天学到了js基础教程3,昨天的课后练习还没来的及做,这个是类似简易日历的小案例,视频还没听完,今晚继续...... 先看效果图: 其实做过前面的Tab选项卡,这个就很好理解了,通过鼠标放在不同月份月 ...

  4. Three.js基础探寻二——正交投影照相机

    本篇主要介绍照相机中的正交投影照相机. 第一篇传送门:Three.js基础探寻一 1.照相机 图形学中的照相机定义了三维空间到二维屏幕的投影方式. 针对投影方式照相机分为正交投影照相机和透视投影照相机 ...

  5. 记录21.07.23 —— Vue.js基础(二)

    Vue基础(二) 过滤器 过滤器作用 全局过滤器 输出结果 私有过滤器 输出结果 把其中一个做点修改 错误信息 自定义指令 全局自定义指令 私有自定义指令 钩子函数 注意:fond-weight是粗细 ...

  6. 前端新人学习笔记-------html/css/js基础知识点(二)

    4月7日学到的知识点:     一:<img src="1.png" alt="美女"/> alt是给图片添加介绍,当图片没加载出来时,会直接显示a ...

  7. 前端新人学习笔记-------html/css/js基础知识点

    即将毕业的软件工程大学生一枚,秋季招聘应聘的是Android,今年来到公司实习,要求做前端开发,所以一切只有现学,现在根据视频来学习,然后开这个博客记录一下自己的学习过程,废话不多说,开写. 4月6日 ...

  8. Node.js基础学习二之POST请求

    本篇介绍下 Node.js post 请求 需求: 用户登录,前端界面输入用户名和密码,点击登录请求后台验证,根据后台反馈的信息做出响应 前端: (1)使用form表单 (2)使用ajax异步请求 服 ...

  9. JS基础知识点(一)

    原始类型 null undefined boolean number string symbol 注意 原始类型存储的都是值,是没有函数可以调用的,但实际上除null和undefined外,其他类型使 ...

随机推荐

  1. 搜索相关性算法在 DiDi Food 中的搜索

    导读:今天给大家分享的主题是搜索匹配问题在 DiDi Food 中的一些探索与应用.本文首先介绍了搜索相关性的一些背景,之后介绍了业界常见的三种匹配模型,以及在DiDi Food业务中的模型效果对比. ...

  2. kafka架构、基本术语、消息存储结构

    1.kafka架构 kafka处理消息大概流程 生产者发送消息给kafka服务器 消费者从kafka服务器(broker)读取消息 kafka服务器依靠zookeeper集群进行服务协调管理 2.ka ...

  3. JPA 中 find() 和 getReference() 的区别

    在查询的时候有两个方法:find()和getReference(),这两个方法的参数以及调用方式都相同.那么这两个方法有什么不一样的呢? find()称为 立即加载,顾名思义就是在调用的时候立即执行查 ...

  4. 在CentOS下利用Docker一键安装seafile

    https://cloud.seafile.com/published/seafile-manual-cn/docker/pro-edition/%E7%94%A8Docker%E9%83%A8%E7 ...

  5. Spring:BeanDefinition&PostProcessor不了解一下吗?

    水稻:这两天看了BeanDefinition和BeanFactoryPostProcessor还有BeanPostProcessor的源码.要不要了解一下 菜瓜:six six six,大佬请讲 水稻 ...

  6. Node.js躬行记(4)——自建前端监控系统

    这套前端监控系统用到的技术栈是:React+MongoDB+Node.js+Koa2.将性能和错误量化.因为自己平时喜欢吃菠萝,所以就取名叫菠萝系统.其实在很早以前就有这个想法,当时已经实现了前端的参 ...

  7. Android学习笔记添加ActionItem

    ActionItem概念 案例仿知乎首页的ActionBar 一.编写布局文件activity_main.xml <?xml version="1.0" encoding=& ...

  8. 添加现有项目到git仓库

    情景: 做了一个项目,需要放到git仓库里 为什么做这个记录? 我们一般的操作是先有仓库, 然后 git clone  到一个空文件夹.     然后再这个空文件夹里加项目文件.  再git push ...

  9. [cpp]C++中的析构函数

    C++中的析构函数 简介 析构函数(Destructors),是对象的成员函数,没有返回值也没有参数,且一个类只有一个析构函数,当对象被销毁的时候调用,被销毁通常有这么几个情况. 函数执行结束 程序执 ...

  10. TCP 粘包拆包

    一.什么是粘包拆包? 粘包拆包是TCP协议传输中一种现象概念.TCP是传输层协议,他传输的是“流”式数据,TCP并不知道传输是哪种业务数据,或者说,并不关心.它只是根据缓冲区状况将数据进行包划分,然后 ...