Javacript二叉树算法剖析
写在前面树是计算机科学中经常用到的一种数据结构,树是一种非线性的数据结构,以分层的方式存储数据。树被用来存储具有层级关系的数据,比如文件系统中的文件;树还被用来存储有序列表。今天,我们来谈一谈一种特殊的树:二叉树。为什么选择二叉树,因为在二叉树上查找非常快,为二叉树添加或删除元素也非常快(而对数组执行添加和删除操作则不是这样)。
树的定义树是一种非线性的数据结构。要理解树的概念及其术语的含义,用一个例子说明是最好的办法。如下图所示就是一棵树,它是若干结点(A、B、C等都是结点)的集合,是由唯一的根(结点A)和若干互不相交的子树(如B、E、F这3个结点组成的树就是一颗子树)组成的。其中每一颗子树又是一棵树,也是由唯一的根结点和若干棵互不相交的子树组成的。由此可知,树的定义是递归 的,即在树的的一种又用到了树的定义。要注意的是,树的结点数目可以为0,当为0时,这棵树为一颗空树,不存在任何结点,这是一种极其特殊的情况。
树的基本术语以下皆以上图为例
结点:图中A,B,C等都是结点,结点不仅包含数据元素,而且包含指向子树的分支。例如,A结点不仅包含数据元素A,而且包含3个指向子树的指针。
结点 ...
JS事件委托
DOM的事件流程DOM的事件流程分为三个阶段:事件捕获阶段、处于目标阶段、事件冒泡阶段。
首先发生的是事件捕获,为截获事件提供了机会。
然后是实际的目标接收到事件。
然后冒泡阶段发生,事件又传播回文档。
第一阶段:事件捕获。所谓的事件捕获就是从最大范围(document)开始,一级一级往下找,知道找到最精确的事件源(触发事件的节点,如图div节点)为止的过程。虽然大多数浏览器都支持事件捕获,但很少有人使用。
第二阶段:事件触发。找到了事件源,接下来就应当执行绑定的相应的事件(如果有的话)。
第三阶段:事件冒泡。事件源执行完事件处理后。这个类型的事件会向祖先节点传递,直到document(包括document)(当然是在事件冒泡没有被阻止的前提下,后续的分析都是基于这个前提下)。也就是说事件源的每一个祖先节点都会触发同类事件。
想象一下,把事件源a触发click事件的处理委托给其祖先节点b。当a节点触发click,事件冒泡到b的时候,b节点也触发click事件,然后b一看事件列表中有一个委托事件,这个委托事件保存了委托节点的选择器,这个选择器所匹配节点就是事件源a,那么b马上执行这个 ...
深度剖析Javascript继承
1. 原型链此种继承方式的基本思想是:利用原型让一个引用类型继承另一个引用类型的属性和方法。
12345678910111213141516function Parent() { this.prop = true } Parent.prototype.getProp = function () { return this.prop } function Son() { this.subProp = false } Son.prototype = new Parent() Son.prototype.getSubProp = function () { return this.subProp } let instance = new Son console.log(instance)
输出如下:
使用此方法实现要注意一点:若在重写原型链之前创建对象,则这个对象不会拥有后面重写原型链之后的方法和属性,我们看下一下下面的代码:
12345678910111213141 ...
JS检测属性位于对象本身还是来自于其原型链
1.in 操作符in 操作符会在通过对象能够访问给定属性时返回true,无论该属性存在于对象本身还是其原型链上。
123456789101112function Person(name) { this.name = name } let obj = new Person('Tom') Person.prototype.gender = 'male' Person.prototype.code = 23 console.log("name" in obj) // true console.log('code' in obj) // true console.log('gender' in obj) // true
2.obj.hasOwnProperty(prop)hasOwnProperty() 方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性。
12345678910function Person(name ...
JS闭包巧用
写在前面什么是闭包?闭包有什么作用?这是我遇到闭包时的第一反应。
闭包在JavaScript高级程序设计(第3版)中是这样描述:闭包是指有权访问另一个函数作用域中的变量的函数。
那么闭包的作用也就很明显了:
可以在函数的外部访问到函数内部的局部变量。
让这些变量始终保存在内存中,不会随着函数的结束而自动销毁。
1.让函数只执行一次123456789101112131415161718function once(fn) { let flag = false; return function() { if (!flag) { flag = true; fn.apply(this, arguments); } } } function log() { console.log(12306); } let c = once(log) c() c() c()
上述代码只输 ...
JS尾递归优化
1. 写在前面本文适合对JS基础有较好的理解的基础上阅读体验最佳,若对JS基础没太搞明白的也难会有点晦涩难懂。不知道什么是尾递归的请去看我上一篇对于尾调用的系统讲解。
2. 递归函数的递归就是在函数中调用自身,递归在我理解有点像数学中的数学归纳法,数学归纳法的原理在于,不停的利用已知的假设进行推导;而函数的递归同理,不停的调用自身。
先上一个小例子,让大家感受一下递归:
12345// 求1 + 2 + 3 + ... + n 的和function sum(n) { if (n <= 2) return 1 return n + sum(n-1)}
3. 递归导致的问题递归用于解决某些问题,比如深层遍历等问题很有效,但是用不好很容易导致栈溢出错误(stack overflow),就算不发生栈溢出,使用不善则会导致严重的性能问题。
我们知道,函数调用会在内存形成一个“调用记录”,又称为“调用帧”(call frame),保存调用位置和内部变量等信息。递归导致的一系列嵌套函数的调用,会产生一系列的调用帧,所有的调用帧就形成了一个“调用栈”(call ...
JS尾调用优化
1. 写在前面上次介绍了什么是尾调用以及怎么准确快速的判别一个函数调用是否为尾调用。那么,我们判别尾调用的意义是什么呢?做什么事情总归有个目的,那么今天我们就来系统的介绍一下尾调用的意义,或者说尾调用有什么用吧。
2. 尾调用优化我们知道,函数的调用会在内存中生成一个“调用帧”(call frame),保存着函数的调用位置和内部变量等信息。如果在函数A的内部调用函数B,那么在A的调用帧上方还会生成一个B的调用帧。等到函数B运行结束,将结果返回到A,B的调用帧才会消失。如果函数B的内部还调用函数C,那么在B的调用帧上方又会生成一个C的调用帧,以此类推。所有的调用帧就形成了一个“调用栈”(call stack)。
我们知道,尾调用是函数的最后一步操作,外部函数的调用位置和其内部变量信息都不会再用到了,所以不需要保留外部函数的调用帧了,直接用内层函数的调用帧取代外层函数的调用帧即可。
如果所有的函数都是尾调用,那么完全可以做到每次执行时的调用帧只有一项,这将大大的节省内存,更不可能发生内存溢出,这就是讨论尾调用的意义所在。
所谓的“尾调用优化”,其实就是保证在函数执行时只保留内层函数的调用帧 ...
JS尾调用
1. 尾调用尾调用(Tail Call)是函数式编程的一个重要的概念,简单的来说就是:某个函数的最后一步是调用另一个函数。
例如下面的例子就是尾调用:
123function f(x) { return g(x)}
2. 尾调用的特点那么,我们来总结一下,尾调用的特点:
首先,尾调用必须满足,函数的最后一步是return另一个函数(如上述例子),这里和闭包有点像;
其次,return 后面的表达式必须仅仅是某个函数的调用,除此之外不能包含其它任何别的操作;
看下面的例子,我们来分析:
1234function f(x) { var a = 3 return a + g(x)}
上面这个例子,就不是尾调用,因为return 后的表达式除了函数调用还包含了加法操作,所以这不是尾调用。
再来看一个例子:
123function f(x) { g(x)}
这仍然不是尾调用,因为上述例子相当于:
1234function f(x) { g(x) return undefined ...
前端跨域有话说
写在前面作为一名前端开发工程师,如果你不懂跨域的话,那实在说不过去啦。几乎所有的前端面试都会问到跨域相关的问题,所以跨域还是十分重要的,同时他也是比较基础的东西,话不多说,今天就来谈一谈跨域。
1.什么是跨域?说到跨域,必须得提一提浏览器的“同源策略”,同源策略/SOP(Same origin policy)是一种约定,由Netscape公司1995年引入浏览器,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSFR等攻击。所谓同源是指”协议+域名+端口”三者相同,即便两个不同的域名指向同一个ip地址,也非同源。
同源策略限制以下几种行为:
Cookie、LocalStorage 和 IndexDB 无法读取
DOM 和 Js对象无法获得
AJAX 请求不能发送
2.跨越问题产生的三大必备条件说到前端的跨域问题,不得不说产生前端跨域问题的必备条件:
浏览器的限制,即请求从前端浏览器发出
发出的请求是XMLHttpRequest请求
请求的资源来自其它域,即域名不同
浏览器发出的请求类型有很多(如下图)
很明显能看到有个选项是XHR,这就是前端的 ...
vue2.5+typescript的采坑系列《一》
写在前面最近笔者在使用vue2.5+typescript构建项目,其实是自己写着玩的,本着探索+分享的目的,在GitHub新建了一个项目,技术选型vue2.5+typescript,因为之前一直没有使用vue+typescript构建项目,所以就想尝试一下,没想到vue+ts却是巨坑多多啊。。。下面开始带大家一起看看,那些年我们一起踩过的坑
1.引入.vue文件不能省略文件扩展名看到上述小标题,你或许会感叹:纳尼?写惯了省略文件后缀名,但是在vue+typescript的项目中,是不能省略.vue文件的扩展名的,起初,笔者引入vue组件,一直报错,而且笔者在webpack的配置中已经配置了extensions中加入了’.vue’,but木有用啊,说起来都是泪,我也是写惯了配置了extensions之后省略文件后缀名的写法,愣是检查了二十多分钟,实在是看不出来哪里错了,最后不得不去某技术网站搜索,终于十分钟之后,终于明白了,原因居然是不能省略文件扩展名,笔者表示无语。这是笔者使用vue2.5+typescript 踩过的第一个坑,特此告知,以免后来者犯同样的错误。。。