GithubHelp home page GithubHelp logo

just-code's Introduction

Hi there 👋

  • 📙 Focusing on JavaScript & Rust
  • 🔭 I'm currently working on JavaScript App
  • 🍀 I'm currently learning and sharing on my blog, welcome~

just-code's People

Contributors

physicshi avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

just-code's Issues

二叉树的前中后序遍历

二叉树的前中后序遍历

我们不能含糊地记说:“中序遍历是先访问左子树,再访问根节点,再访问右子树”。这么描述是不准确的,容易产生误导。

事实上,无论是前、中、后序遍历,都是先访问根节点,再访问它的左子树,再访问它的右子树

我们都会对每一个节点「做一些事情」,区别在于做一些事情的时机不同

比如中序遍历,它是将处理当前节点放在了访问完它的左子树之后。比方说,打印一下节点值,就会产生「左 根 右」的打印顺序。

image

前、中、后序遍历都是基于DFS,节点的访问顺序如上图所示,每个节点有三个不同的驻留阶段,即每个节点会被经过三次:

在递归它的左子树之前。
在递归完它的左子树之后,在递归它的右子树之前。
在递归完它的右子树之后。
我们将处理当前节点,放在这三个时间点之一,就分别对应:前、中、后序遍历。

在递归它的左子树之前,处理当前节点 ---- 前序遍历
在递归完它的左子树之后,在递归它的右子树之前,处理当前节点 ---- 中序遍历
在递归完它的右子树之后,处理当前节点 ---- 后序遍历

二叉树的前序遍历和中序遍历思路是一致的,对于迭代的方式来讲,区别就是前序遍历是入栈的顺序(在stack进行push的时候,结果数组push),中序遍历是出栈的顺序(在stack进行pop的时候,结果数组push)。
对于后序遍历,需要先遍历右子树,再遍历左子树, 和前中序遍历对比就是反着来,在stack进行push的时候,队列unshift的顺序就是后序遍历的顺序。

二叉树的前序遍历

var preorderTraversal = function(root) {
    const res = [];
    const stack = [];
    while(root || stack.length){
        while(root){
            res.push(root.val);
            stack.push(root);
            root=root.left
        }
        root=stack.pop()
        root=root.right
    }
    return res
};

二叉树的中序遍历

const inorderTraversal = (root) => {
    if(!root) return [];
  const res = [];
  const stack = [];
  while(root || stack.length){
      while(root){
          stack.push(root)
          root = root.left;
      }
     root = stack.pop();
     res.push(root.val);
     root = root.right;
  }
  return res;
};

二叉树的后序遍历

var postorderTraversal = function(root) {
  // 初始化数据
    const res =[];
    const stack = [];
    while (root || stack.length){
      while(root){
        stack.push(root);
        res.unshift(root.val);
        root = root.right;
      }
      root = stack.pop();
      root = root.left;
    }
    return res;
};

操作cookie

操作cookie

所有基于cookie的操作都是document.cookie

如果要新增cookie,就可以document.cookie="cookiename=cookievalue"(更新cookie只需要document.cookie="cookiename=newCookievalue"就会进行覆盖)

注意设置为HttpOnlycookie我们是没办法用js拿到的

获取cookie

function get_cookie(Name) {
   var search = Name + "="//查询检索的值
   var returnvalue = "";//返回值
   if (document.cookie.length > 0) {
     sd = document.cookie.indexOf(search);
     if (sd!= -1) {
        sd += search.length;
        end = document.cookie.indexOf(";", sd);
        if (end == -1)
         end = document.cookie.length;
         //unescape() 函数可对通过 escape() 编码的字符串进行解码。
        returnvalue=unescape(document.cookie.substring(sd, end))
      }
   }
   return returnvalue;
}
//使用方式:
get_cookie("popped");

给cookie设置终止日期

//获取当前时间
var date=new Date();
var expiresDays=10;
//将date设置为10天以后的时间
date.setTime(date.getTime()+expiresDays*24*3600*1000);
//将userId和userName两个cookie设置为10天后过期
document.cookie="userId=828; userName=hulk; expires="+date.toGMTString();

删除cookie

删除一个cookie只需要给cookie设置一个过去的时间;

//获取当前时间
var date=new Date();
//将date设置为过去的时间
date.setTime(date.getTime()-10000);
//将userId这个cookie删除
document.cookie="userId=828; expires="+date.toGMTString();

其中GMT_String是以GMT格式表示的时间字符串,这条语句就是将userId这个cookie设置为GMT_String表示的过期时间,超过这个时间,cookie将消失,不可访问。

完整代码

var cookie = {
        set:function(key,val,time){//设置cookie方法
            var date=new Date(); //获取当前时间
            var expiresDays=time;  //将date设置为n天以后的时间
            date.setTime(date.getTime()+expiresDays*24*3600*1000); //格式化为cookie识别的时间
            document.cookie=key + "=" + val +";expires="+date.toGMTString();  //设置cookie
        },
        get:function(key){//获取cookie方法
            /*获取cookie参数*/
            var getCookie = document.cookie.replace(/[ ]/g,"");  //获取cookie,并且将获得的cookie格式化,去掉空格字符
            var arrCookie = getCookie.split(";")  //将获得的cookie以"分号"为标识 将cookie保存到arrCookie的数组中
            var tips;  //声明变量tips
            for(var i=0;i<arrCookie.length;i++){   //使用for循环查找cookie中的tips变量
                var arr=arrCookie[i].split("=");   //将单条cookie用"等号"为标识,将单条cookie保存为arr数组
                if(key==arr[0]){  //匹配变量名称,其中arr[0]是指的cookie名称,如果该条变量为tips则执行判断语句中的赋值操作
                    tips=arr[1];   //将cookie的值赋给变量tips
                    break;   //终止for循环遍历
                }
            }
            return tips;
        },
        delete:function(key){ //删除cookie方法
            var date = new Date(); //获取当前时间
            date.setTime(date.getTime()-10000); //将date设置为过去的时间
            document.cookie = key + "=v; expires =" +date.toGMTString();//设置cookie
        }
    }

647 回文子串

回文子串

子串连续。子序列可以不连续

给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。

具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。

题解

这个题和最长回文子序列有一些像,就是看我们的重点都是扩展。
布尔类型的dp[i][j]:表示区间范围[i,j] (注意是左闭右闭)的子串是否是回文子串,如果是dp[i][j]true,否则为false

s[i]s[j]不相等,那没啥好说的了,dp[i][j]一定是false
s[i]s[j]相等时,这就复杂一些了,有如下三种情况
情况一:下标ij相同,同一个字符例如a,当然是回文子串
情况二:下标ij相差为1,例如aa,也是文子串
情况三:下标:ij相差大于1的时候,例如cabac,此时s[i]s[j]已经相同了,我们看ij区间是不是回文子串就看aba是不是回文就可以了,那么aba的区间就是 i+1j-1区间,这个区间是不是回文就看dp[i + 1][j - 1]是否为true

var countSubstrings = function (s) {
    const dp = new Array(s.length);
    let count = 0
    for (let i = s.length-1; i >=0; i--) {
        dp[i] = new Array(s.length).fill(false)
        for (let j = i; j < s.length; j++) {
            if (i === j) { dp[i][j] = true; count++ }
            else if (j - i === 1 && s[j] === s[i]) {
                dp[i][j] = true;
                count++
            } else if (j - i > 1 && s[j] === s[i] && dp[i + 1][j - 1]) {
                dp[i][j] = true
                count++
            } 
        }
    }
    return count
}

认证

OAuth和token

OAuth规定授权流程,Token授权中的一环作为信息的载体

OAuth就是一种授权机制,核心就是向第三方应用颁发令牌,同意第三方应用进入系统获取数据。

认证服务器同意授权第三方应用进入系统获取数据,系统会产生一个短期令牌,用来代替密码,供第三方使用。

OAuth 2.0 规定了四种获得令牌的流程。

  • 授权码(authorization-code)
  • 隐藏式(implicit)
  • 密码式(password):
  • 客户端凭证(client credentials)

主要说一下授权码,授权码主要指的是第三方应用先申请一个授权码,然后再用该码获取令牌。

请求授权码->返回授权码->请求令牌->返回令牌

其实很多微信授权登陆就是用的授权码的方式。

  • A 网站提供一个链接,用户点击后就会跳转到 B 网站,授权用户数据给 A 网站使用

  • 用户跳转后,B 网站会要求用户登录,然后询问是否同意给予 A 网站授权。用户表示同意,这时 B 网站就会跳回redirect_uri参数指定的网址。跳转时,会传回一个授权码

  • A 网站拿到授权码以后,就可以在后端,向 B 网站请求令牌

  • B 网站收到请求以后,就会颁发令牌。具体做法是向redirect_uri指定的网址,发送一段 JSON 数据

    • access_token字段就是令牌,A 网站在后端拿到

token

两种tokenaccess tokenrefresh token

使用双令牌实现无感知登录

  • Refresh Token 过期时间设置较长,用于更新 Access Token
  • Access Token 过期时间设置较短

access token

服务端生成

  • 调API时所需要的资源凭证(请求api时也要携带token

  • 简单 token 的组成: uid(用户唯一的身份标识)、time(当前时间的时间戳)、sign(签名,token 的前几位以哈希算法压缩成的一定长度的十六进制字符串)

  • 使用:加上一个Authorization字段,令牌就放在这个字段里面。

refresh token

令牌到期前,用户使用 refresh token 发一个请求,去更新令牌

https://b.com/oauth/token?
  grant_type=refresh_token&
  client_id=CLIENT_ID&
  client_secret=CLIENT_SECRET&
  refresh_token=REFRESH_TOKEN

上面 URL 中,grant_type参数为refresh_token表示要求更新令牌,client_id参数和client_secret参数用于确认身份,refresh_token参数就是用于更新令牌的令牌。

B 网站验证通过以后,就会颁发新的令牌

基于cookie的session

  • session 认证流程:
    • 用户第一次请求服务器的时候,服务器根据用户提交的相关信息,创建对应的 Session
    • 请求返回时将此 Session 的唯一标识信息 SessionID 返回给浏览器
    • 浏览器接收到服务器返回的 SessionID 信息后,会将此信息存入到 Cookie 中,同时 Cookie 记录此 SessionID 属于哪个域名
    • 当用户第二次访问服务器的时候,请求会自动判断此域名下是否存在 Cookie 信息,如果存在自动将 Cookie 信息也发送给服务端,服务端会从 Cookie 中获取 SessionID,再根据 SessionID 查找对应的 Session 信息,如果没有找到说明用户没有登录或者登录失效,如果找到 Session 证明用户已经登录可执行后面操作。

根据以上流程可知,SessionID 是连接 Cookie 和 Session 的一道桥梁,大部分系统也是根据此原理来验证用户登录状态。

cookie扫盲

cookie的位置

cookie并不是只存在浏览器里,实际上,Cookies 相关的内容还可以存在本地文件里,就比如说 Mac 下的 Chrome,存放目录就是 ~/Library/Application Support/Google/Chrome/Default,里面会有一个名为 Cookies 的数据库文件。

存放在本地的好处就在于即使你关闭了浏览器,Cookie 依然可以生效。

cookie的设置

cookie

因为http是无状态的,所以服务端会通过session机制来进行身份验证:

  1. 用户输入其登录信息

  2. 服务器验证信息是否正确,并创建一个session,然后将其存储在数据库中

  3. 服务器为用户生成一个sessionId(随机字符串),在响应头里面添加一个 Set-Cookie 字段

  4. 浏览器收到响应后保存下 Cookie

  5. 之后对该服务器每一次请求中都通过 Cookie 字段将 Cookie 信息发送给服务器,服务器会根据数据库验证sessionId,如果有效,则接受请求

但是对于用户量大的场景,大量的sessionId对服务器来说也是很大的压力。

JWT

JSON web token简称JWT,是目前最流行的跨域认证解决方案。

  1. 用户输入其登录信息

  2. 服务器验证信息是否正确,并返回已签名的token

  3. token储在客户端,例如存在localStoragecookie

  4. 之后的HTTP请求都将token添加到请求头里Authorization这个字段

  5. 服务器解码JWT,并且如果令牌有效,则接受请求

  6. 一旦用户注销,令牌将在客户端被销毁,不需要与服务器进行交互一个关键是,令牌是无状态的。后端服务器不需要保存令牌或当前session的记录。`

  • JWT 的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源。比如用在用户登录上。
  • 可以使用 HMAC 算法或者是 RSA 的公/私秘钥对 JWT 进行签名
  • 因为 JWT 是内部包含了一些会话信息,因此减少了需要查询数据库的需要
  • 因为 JWT 并不使用 Cookie 的,所以你可以使用任何域名提供你的 API 服务而不需要担心跨域资源共享问题(CORS)
  • 因为用户的状态不再存储在服务端的内存中,所以这是一种无状态的认证机制
  • 一旦签发,到期前始终有效,除非存在额外逻辑

JWT和session

相同点是,它们都是存储用户信息;然而,Session是在服务器端的,而JWT是在客户端的。

Session方式存储用户信息的最大问题在于要占用大量服务器内存,增加服务器的开销。

而JWT方式将用户状态分散到了客户端中,可以明显减轻服务端的内存压力。

Session的状态是存储在服务器端,客户端只有session id;而Token的状态是存储在客户端。

cookie的属性

Domain 指定了 Cookie 可以送达的主机名(domain就是cookie绑定的域)。假如没有指定,那么默认值为当前文档访问地址中的主机部分(但是不包含子域名)。

像淘宝首页设置的 Domain 就是 .taobao.com,这样无论是 a.taobao.com 还是 b.taobao.com 都可以使用 Cookie。

在这里注意的是,不能跨域设置 Cookie,比如阿里域名下的页面把 Domain 设置成百度是无效的:

Set-Cookie: qwerty=219ffwef9w0f; Domain=baidu.com; Path=/; Expires=Wed, 30 Aug 2020 00:00:00 GMT

Path

Path 指定了一个 URL 路径,这个路径必须出现在要请求的资源的路径中才可以发送 Cookie 首部。比如设置 Path=/docs/docs/Web/ 下的资源会带 Cookie 首部,/test 则不会携带 Cookie 首部。

Domain 和 Path 标识共同定义了 Cookie 的作用域:即 Cookie 应该发送给哪些 URL。

HTTPOnly

设置 HTTPOnly 属性可以防止客户端脚本通过 document.cookie 等方式访问 Cookie,有助于避免 XSS 攻击。

SameSite

SameSite 是最近非常值得一提的内容,因为 2 月份发布的 Chrome80 版本中默认屏蔽了第三方的 Cookie,这会导致阿里系的很多应用都产生问题,为此还专门成立了问题小组,推动各 BU 进行改造。

作用

我们先来看看这个属性的作用:

SameSite 属性可以让 Cookie 在跨站请求时不会被发送,从而可以阻止跨站请求伪造攻击(CSRF)。

第三方Cookie:由当前a.com页面发起的请求的 URL 不一定也是 a.com 上的,可能有 b.com 的,也可能有 c.com 的。我们把发送给 a.com 上的请求叫做第一方请求(first-party request),发送给 b.com 和 c.com 等的请求叫做第三方请求(third-party request),第三方请求和第一方请求一样,都会带上各自域名下的 cookie,所以就有了第一方cookie(first-party cookie)和第三方cookie(third-party cookie)的区别。上面提到的 CSRF 攻击,就是利用了第三方 cookie可以携带发送的特点 。

比如百度的网页上有一张淘宝的图片,请求这个图片就是第三方请求。

只有当前网页的 URL 与请求目标一致,才是第一方cookie

属性值

SameSite 可以有下面三种值:

  1. Strict 仅允许第一方请求携带 Cookie,即浏览器将只发送相同站点请求的 Cookie,即当前网页 URL 与请求目标 URL 完全一致。
  2. Lax 允许部分第三方请求携带 Cookie
  3. None 无论是否跨站都会发送 Cookie

之前默认是 None 的,Chrome80 后默认是 Lax。

跨域和跨站

首先要理解的一点就是跨站和跨域是不同的。同站(same-site)/跨站(cross-site)」和第一方(first-party)/第三方(third-party)是等价的。但是与浏览器同源策略(SOP)中的「同源(same-origin)/跨域(cross-origin)」是完全不同的概念。

同源策略的同源是指两个 URL 的协议/主机名/端口一致。例如,www.taobao.com/pages/...,它的协议是 https,主机名是 www.taobao.com,端口是 443。

同源策略作为浏览器的安全基石,其「同源」判断是比较严格的,相对而言,Cookie中的「同站」判断就比较宽松:只要两个 URL 的 eTLD+1 相同即可,不需要考虑协议和端口。其中,eTLD 表示有效顶级域名,注册于 Mozilla 维护的公共后缀列表(Public Suffix List)中,例如,.com、.co.uk、.github.io 等。eTLD+1 则表示,有效顶级域名+二级域名,例如 taobao.com 等。

举几个例子,www.taobao.com 和 www.baidu.com 是跨站,www.a.taobao.com 和 www.b.taobao.com 是同站,a.github.io 和 b.github.io 是跨站(注意是跨站)。

DOM事件流

DOM事件流

一个事件被触发,会经历三个阶段,事件捕获、目标阶段、事件冒泡。

捕获的传播路径就是从document(可以看做整个html文档)出发,document ->html ->body->...->e.target的父元素;

目标阶段就是事件目标元素上发生;

冒泡阶段:e.target的父元素->...->body->html->document

DOM2级事件

事件绑定有DOM0级(on+type)和DOM2级(addEventListener)

在DOM0级事件只支持在冒泡阶段回调;

在DOM2级事件中规定的事件流同时支持了事件捕获阶段和事件冒泡阶段;作为开发者,我们可以选择事件处理函数在哪一个阶段被调用

addEventListener(eventType, function, useCapture)

其中第三个参数表示事件处理函数在哪个阶段被回调:默认是false,在冒泡阶段被回调。

  • addEventListener

    • 可以绑定多个事件,不会覆盖

      • chrome91之后是按照先捕获再冒泡的顺序执行的,之前是按照按代码顺序
    • 可以支持事件的冒泡和捕获:通过传入的第三个参数控制,默认为false表示事件冒泡;true表示事件捕获

  • onclick

    • 只支持冒泡阶段

    • 一次只能绑定一个事件处理,会覆盖

经过的元素

e.currentTarget

阻止事件的默认行为

e.preventDefault()

取消冒泡

e.stopPropagation()e.cancelBubble=true

目标元素

e.target

5 最长回文子串

最长回文子串

给你一个字符串 s,找到 s 中最长的回文子串。

题解

重点也是扩展,dp[i][j] 表示 s 中从 ij(包括 ij)是否可以形成回文

对于回文类的题,都是扩展的思路;对于子串,dp[i][j]是区间[i,j]是否可以形成回文串;对于子序列,dp[i][j]是从ij的最长子序列

var longestPalindrome = function (s) {
    if (s.length === 0 || !s) { return "" }
    const dp = []
    let res = s[0]
    for (let i = s.length - 1; i >= 0; i--) {
        dp[i] = []
        for (let j = i; j < s.length; j++) {
            if (i === j) dp[i][j] = true
            else if (j-i===1 && s[i]===s[j]) {
                dp[i][j] = true
            } else if(s[i] === s[j] && dp[i + 1][j - 1]){
                dp[i][j] = true
            }
            if (dp[i][j] && j - i + 1 > res.length) {
                res = s.slice(i, j + 1)
            }
        }
    }
    return res
};

516 最长回文子序列

最长回文子序列

给你一个字符串 s ,找出其中最长的回文子序列,并返回该序列的长度。

子序列定义为:不改变剩余字符顺序的情况下,删除某些字符或者不删除任何字符形成的一个序列。

题解

dp[i][j]表示从ij的最长回文子序列;
重点是扩展,如果一个字符串是回文串,左右加一个一样的,也是回文串:dp[i][j]=dp[i+1][j-1]+2
如果左右加了不一样的,那么dp[i][j]要么是dp[i+1][j]要么是dp[i][j-1]

const longestPalindromeSubseq = (s) => {
    const dp = []
    for(let i= s.length-1;i>=0;i--){
        dp[i]=new Array(s.length).fill(0)
        for(let j=i;j<s.length;j++){
             if(i===j) {dp[i][j]=1}
            else if(s[i]===s[j]){
                dp[i][j]=dp[i+1][j-1]+2
            } else{
                dp[i][j] = Math.max(dp[i+1][j],dp[i][j-1])
            }  
        }
    }
    return dp[0][s.length-1]
}

React版本的权限路由

react版本的路由守卫

前置知识

本身现在用react或者vue构建的都是单页面富应用,在使用一个html的基础上,页面切换实质是组件的切换。

react-router基于history库。

  • history库提供了两种创建路由模式的apicreateBrowserHistory 或者 createHashHistory 创建出不同的 history 对象),以及监听路由变化(popstateonhashchange)、改变路由(pushreplace

  • react-router包提供了RouteSwitchRedirect

  • react-router-dom包提供了NavLinkLinkBrowserRouterHashRouter

路由守卫

核心思路:利用Provider传递上下文

/* 模拟网络请求 */
function getRootPermission(){
    return new Promise((resolve)=>{
        resolve({
            code:200, /* 数据模拟只有编写文档,和编写标签模块有权限,文档列表没有权限 */
            data:[ '/config/index'  , '/config/writeTag' ]
        })
    })
}

/* 路由根部组件 rootRoute.tsx */
const Permission = React.createContext([])
export default function Index(){
    const [ rootPermission , setRootPermission ] = React.useState([])
    React.useEffect(()=>{
        /* 获取权限列表 */
        getRootPermission().then(res=>{
            console.log(res,setRootPermission)
            const { code , data } = res as any
            code === 200 && setRootPermission(data)
        }) 
    },[])
    return <Permission.Provider value={rootPermission} >
         <RootRouter/>
    </Permission.Provider>
}

/* 路由权限组件 */
export function PermissionRouter(props){
    const permissionList = useContext(Permission) /* 消费权限列表 */
    const isMatch = permissionList.indexOf(props.path) >= 0 /* 判断当前页面是否有权限 */
    return isMatch ? <Route {...props}  /> : <Redirect to={'/config/NoPermission'}  />
}

<Switch>
    <PermissionRouter   path={'/config/index'} component={WriteDoc}   />
    <PermissionRouter   path={'/config/docList'} component={DocList}   />
    <PermissionRouter   path={'/config/writeTag'} component={WriteTag}   />
    <PermissionRouter   path={'/config/tagList'} component={TagList}   />
    <Route path={'/config/NoPermission'}  component={NoPermission}  />
</Switch>

160 相交链表

相交链表

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。

题解

双指针:一个链表遍历结束,移到另一个链表重新遍历,这样两个链表走的长度肯定是一样的,就会在第二次遍历时相遇,得到相交结点

var getIntersectionNode = function(headA, headB) {
    let a=headA,b=headB
    while(a!==b){
        a=a===null?headB:a.next;
        b=b===null?headA:b.next;
    }
    return a
};

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.