【JavaScript】要点知识的个人总结(1)
米娜桑,哦哈哟~
该篇章主要基于链接中的参考内容和代码测试得出的结论,面向具有一定基础的前端开发者。如有错误,请指出与包涵。
原型链的解释
白话翻译:原型链就相当于对象上的一个链条,通过隐性原型属性__proto__ 将其他相关的属性绑定以达到引用的效果,其链条的终点就是 Object.prototype,这就是原型链。
class.prototype === obj.__proto__
Object.create()声明的对象
Object.create({name: 1}) 相当于在这个对象的链条上再增加一条链条__proto__,所以 Object.create(null) 的结果是没有任何隐藏属性非常纯净且可高度定制的 {} (一般用于一些开源项目),当然通过
let a = {}
a.__proto__ = null
也可以获得纯净的对象,不过生成的性能一般,通过以下代码实现 Object.create()。
function create(obj) {
let F = function () {}
F.prototype = obj
return new F()
}
function create(obj) {
let a = {}
a.__proto = obj
return a
}
Function.prototype.bind() 的特别之处
通过 Function.prototype.bind() 声明的函数不是 Function 的实例,故不存在class.prototype === obj.__proto__;不过可以通过
Object.prototype.toString.call()可以得知它依然是一个函数。
并与 Function.prototype 是一致的。
new
- new 的过程如下
创建有 Object 函数实例出来的空对象;
将该对象绑定到相应的构造函数上,其此为 this 的上下文,如果没有返回对象的话,就返回 this
function zxNew() {
let fn = arguments[0]
if (typeof fn !== 'function') {
throw new TypeError('first param must be a function')
}
Array.prototype.shift.call(arguments)
let obj = Object.create(null);
obj.__proto__ = fn.prototype;
let res = fn.apply(obj, arguments);
return res instanceof Object ? res : obj
}
原型链继承的方式
- https://juejin.im/post/5b654e88f265da0f4a4e914c
- https://github.com/mqyqingfeng/Blog/issues/16
- https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Classes/extends
原型继承
function Parent () {
this.name = 'temax'
}
Parent.prototype.fn = function () {
console.log(233)
}
function Child () {}
Child.prototype = new Parent()
let child = new Child()
缺点:属性共享、不能传参
经典继承——使用 call 方法借用构造函数的方式
function Parent (name) {
this.name = name
}
Parent.prototype.fn = function () {
console.log(233)
}
function Child (name) {
Parent.call(this, name)
}
let child = new Child('temax')
缺点:不能继承prototype上的属性
组合继承——即原型链继承和经典继承的结合
function Parent(name) {
this.name = name
}
Parent.prototype.fn = function () {
console.log(233)
}
function Child(name) {
Parent.call(this, name)
}
Child.prototype = new Parent()
Child.prototype.constructor = Child
let child = new Child(1)
缺点:父类函数执行两次
组合继承的优化
function Parent(name) {
this.name = name
}
function.prototype.fn = function () {
console.log(233)
}
function Child(name) {
Parent.call(this, name)
}
Child.prototype = Parent.prototype
child.prototype.constructor = Child
let child = new Child()
缺点:prototype 的保存地址其实就是父级的保存地址,也就是说如果改变 child.prototype 会直接影响到父级的 prototype,所以需要加一个__proto__进行隔离
寄生组合的方式——比较理想的组合方式
function Parent(name) {
this.name = name
}
function.prototype.fn = function () {
console.log(233)
}
function Child(name) {
Parent.call(this, name)
}
Child.prototype = Object.create(Parent.prototype)
child.prototype.constructor = Child
class的继承方式
class Parent{
}
class Child extends Parent{
}
this的指向
- this永远指向最后调用的对象。
也就是说 a.b.c.fn(),那么 fn 里面的 this 的指向 c 的属性。
如果令 d = b.c.fn;则 a.d() 中的 this 是指向 a 的。 - setTimeout 的 function 中的 this 是指向 window 对象的。因为 setTimeout 实则为 window.setTimeout。
- 可通过定义this的变量来调用或使用 apply/call/bind 的方式改变 this 指向。
箭头函数的this
- 自身没有声明 this,所以会在作用域链上找最近的 this。
- 不可以当作构造函数。
- 不能用作 Generator 函数。
- 不能使用 arguments,arguments 可以用 ...rest 取代。
apply、call、bind
- 可以通过 apply 和 call 对对象进行调用, 使得函数内的 this 绑定到该调用的值。
- apply 和 call 的区别就是传入方式的不一样,前者是数组传参的方式,后者是直接传参的方式。
- bind 跟 call 一致,只不过 bind 会创建并返回一个新的函数。
- 实现机制如下
call (apply同理,只是改变参数的形式)
Function.prototype.call = function(){
if (typeof this !== 'function') {
throw new TypeError('Called object must be a function')
}
let obj = Object(arguments[0]) || window,
obj.fn = this,
result;
arr = arguments.slice();
arr.shift();
result = obj.fn(...arr)
delete obj.fn
return result
}
bind
如果要模仿 Function.prototype.bind() 得到的函数 fn,需要设置
fn.__proto__ = Object.prototype
fn.prototype = undifned
Function.prototype.bind1 = function () {
let fn = this;
if(typeof fn === 'function'){
if(fn !== Function.prototype){
let obj = Object(arguments[0]) || window,
args = Array.prototype.slice.call(arguments,1),
TempFn = function () {},
FnBind = function () {
//当使用new来构造函数(通过bind得到的)时,会忽略bind(obj)的obj
//对于这个的理解,应该结合new和call的实现来解释,这个时候的this是指向 Object.create(null),如果没有设置prototype的话
//因为原生得到的(new fn1) instanceof fn1 为 true,
// (new fn1) instanceof fn 也为 true,
//又因为 TempFn.prototype = fn.prototype;(只要满足 ojb.__proto__ === class.prototype.constructor)
//所以用改造后得到的 (new fn2) instanceof TempFn 为 true
//简单来说就是,当为构造函数的时候,这里的this是等于指向实例,而实例 instanceof fn1 为true, 实例 instanceof fn也为true,
//而只要满足 ojb.__proto__ === class.prototype
//那么 instanceof 得到的就是 true,所以可以用 this instanceof TempFn 来表示
let bindArgs = Array.prototype.slice.call(arguments);
return fn.apply(this instanceof TempFn ? this : obj, args.concat(bindArgs))
};
TempFn.prototype = fn.prototype;
FnBind.prototype = new TempFn;
return FnBind
}else {
fn.prototype = undefined
fn.__proto__ = Object.prototype
return fn
}
}else {
throw TypeError('Called object must be a function')
}
}
事件循环
- https://segmentfault.com/a/1190000004322358#comment-area
- https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/EventLoop#运行时概念
- https://juejin.im/post/59e85eebf265da430d571f89
- https://segmentfault.com/a/1190000011198232
事件循环是和消息队列共同实现JS同步异步运行的机制。消息队列先进先出,一个循环里,先从队列中取出宏任务并执行,如果存在异步任务,便会将其回调函数注册发放到消息队列当中;再执行微任务,直到相应的执行栈为空为止;执行完后启动新的循环;事件循环是js的运行机制。
事件循环还分宏任务和微任务
- 宏任务:整体代码;setTimeout;setInterval,宏任务消息队列
微任务:Promise; process.nextTick,微任务消息队列 - 通过以下代码测试
setTimeout(function(){
console.log('定时器开始啦')
});
new Promise(function(resolve){
console.log('马上执行for循环啦');
for(var i = 0; i < 10000; i++){
i == 99 && resolve();
}
}).then(function(){
console.log('执行then函数啦')
});
console.log('代码执行结束');
节点操作
- 添加节点可以通过 appendChild
- 通过 innerText 对节点添加文本信息
- 也可以通过 innerHTML 对节点添加 html 文本
- 利用 document.createElement 添加对应元素。
- 通过 node.addEventListener(type,listener,capture) 添加事件。第三参数为是否捕获,默认false,即冒泡
- document 为最大的节点。
- 当然也可以通过 DocumentFragment 来减少 dom 操作
- 以下是 ul 元素添加 li 元素的例子
let ul = document.querySelector('.cul');
// let ul = document.querySelectorAll('.cul')[0];
let a = '';
function myFunction(e){console.log(e)}
for(let i=0;i<5;i++){
a += `<li onclick='myFunction(${i})'>${i}</li>`
}
ul.innerHTML = a
let ul = document.querySelectorAll('.cul')[0];
for(let i=0;i<5;i++){
let a = document.createElement('li');
a.innerHTML = i
a.addEventListener('click',function () {
console.log(i)
})
ul.appendChild(a)
}
let ul = document.querySelectorAll('.cul')[0];
let fragment = document.createDocumentFragment()
for(let i=0;i<5;i++){
let a = document.createElement('li');
a.innerHTML = i
a.addEventListener('click',function () {
console.log(i)
})
fragment.appendChild(a)
}
ul.appendChild(fragment)
【JavaScript】要点知识的个人总结(1)的更多相关文章
- Javascript基础知识总结一
Javascript基础知识总结一 <!DOCTYPE html> <html> <head lang="en"> <meta chars ...
- javascript 要点
javascript 要点 1 JavaScript:写入 HTML 输出 document.write("<h1>This is a heading</h1>&qu ...
- 学习javascript基础知识系列第二节 - this用法
通过一段代码学习javascript基础知识系列 第二节 - this用法 this是面向对象语言中的一个重要概念,在JAVA,C#等大型语言中,this固定指向运行时的当前对象.但是在javascr ...
- 学习javascript基础知识系列第三节 - ()()用法
总目录:通过一段代码学习javascript基础知识系列 注意: 为了便于执行和演示,建议使用chrome浏览器,按F12,然后按Esc(或手动选择)打开console,在console进行执行和演示 ...
- javascript常用知识点集
javascript常用知识点集 目录结构 一.jquery源码中常见知识点 二.javascript中原型链常见的知识点 三.常用的方法集知识点 一.jquery源码中常见的知识点 1.string ...
- 七个开法者经常忽略或误用的JavaScript基本知识
七个开法者经常忽略或误用的JavaScript基本知识 翻译自 http://tech.pro/tutorial/1453/7-javascript-basics-many-developers-ar ...
- JavaScript 基础知识 - BOM篇
前言 本篇文章是JavaScript基础知识的BOM篇,如果前面的<JavaScript基础知识-DOM篇>看完了,现在就可以学习BOM了. 注意: 所有的案例都在这里链接: 提取密码密码 ...
- JavaScript数组知识
JavaScript数组知识 <!DOCTYPE html> <html lang="en"> <head> <meta charset= ...
- JS控制语句 编程练习 学生数据,分别是姓名、性别、年龄和年级,接下来呢,我们要利用JavaScript的知识挑出其中所有是大一的女生的的名字哦。
编程练习 在一个大学的编程选修课班里,我们得到了一组参加该班级的学生数据,分别是姓名.性别.年龄和年级,接下来呢,我们要利用JavaScript的知识挑出其中所有是大一的女生的的名字哦. 学生信息如下 ...
- AST抽象语法树——最基础的javascript重点知识,99%的人根本不了解
AST抽象语法树——最基础的javascript重点知识,99%的人根本不了解 javascriptvue-clicommonjswebpackast 阅读约 27 分钟 抽象语法树(AST),是一 ...
随机推荐
- 7-5 jmu-python-分段函数1 (10 分)
本题目要求计算下列分段函数f(x)的值(x为从键盘输入的一个任意实数): 输入格式: 直接输入一个实数给 x,没有其他任何附加字符. 输出格式: 在一行中按“f(x)=result”的格式输出,其中x ...
- 小程序Echarts 构建中国地图并锚定区域点击事件
小程序Echarts 构建中国地图并锚定区域点击事件 Step1 效果展示 使用的绘图框架为 Echarts for Wexin 具体API文档地址请点击 ----> Step2 条件准备 1. ...
- xadmin安装和配置
1.在虚拟环境pip install xadmin 2.安装完成之后在settings.py的install app里面添加xadmin和crispy_forms 3.在主项目url里面把原来的adm ...
- openwrt sdk 编译工具 及 hello world
需要先在 make menuconfig 中打开 sdk make -j4在 bin\targets\ramips\mt7620生成一个openwrt-sdk-ramips-mt7620_gcc-7. ...
- Golang package轻量级KV数据缓存——go-cache源码分析
作者:Moon-Light-Dream 出处:https://www.cnblogs.com/Moon-Light-Dream/ 转载:欢迎转载,但未经作者同意,必须保留此段声明:必须在文章中给出原文 ...
- 【Oracle】RAC的多实例数据迁移至单机的多实例。
思路:一般的思路可以通过RMAN进行数据的恢复.由于数据库可以停机,因此,这次试用数据泵(expdp,impdp)进行数据 的导入导出. 1.源数据库导出 通过编写导出shell脚本导出数据,如下: ...
- 一文深入了解史上最强的Java堆内缓存框架Caffeine
它提供了一个近乎最佳的命中率.从性能上秒杀其他一堆进程内缓存框架,Spring5更是为了它放弃了使用多年的GuavaCache 缓存,在我们的日常开发中用的非常多,是我们应对各种性能问题支持高并发的一 ...
- Lucene查询语法汇总
目录 一.单词查询 二.通配符查询 三.模糊查询 四.近似查询 五.范围查询 六.优先级查询 七.逻辑操作 八.括号分组 九.转义特殊字符 Lucene是目前最为流行的开源全文搜索引擎工具包,提供了完 ...
- 源码解读 Golang 的 sync.Map 实现原理
简介 Go 的内建 map 是不支持并发写操作的,原因是 map 写操作不是并发安全的,当你尝试多个 Goroutine 操作同一个 map,会产生报错:fatal error: concurrent ...
- JAVA和C#中数据库连接池原理与应用
JAVA和C#中数据库连接池原理 在现在的互联网发展中,高并发成为了主流,而最关键的部分就是对数据库操作和访问,在现在的互联网发展中,ORM框架曾出不穷, 比如:.Net-Core的EFCore.Sq ...