Code Monkey home page Code Monkey logo

blog's Introduction

blog's People

Contributors

panyefan avatar

Stargazers

 avatar

Watchers

 avatar

blog's Issues

原型与原型链

1、原型链(proto):JS对象通过__proto__ 指向父类的原型对象,直到指向Object的原型对象为止,这样就形成了一个原型指向的链条, 即原型链。

2、原型(prototype):JS对象中都包含了一个” prototype”内部属性,这个属性所对应的就是该对象的原型。原型的主要作用就是为了实现继承与扩展对象。

3、对象的 hasOwnProperty() 来检查对象自身中是否含有该属性

4、使用 in 检查对象中是否含有某个属性时,如果对象中没有但是原型链中有,也会返回 true

5、笔试题:new 的执行过程:

  • 创建一个新对象。
  • 将新对象的 proto 指向构造函数的 prototype,这样新对象就可以访问到构造函数原型中的属性。
  • 使用 apply,改变构造函数 this 的指向到新对象,这样新对象就可以访问到构造函数中的属性。
  • 返回 this 指向的新对象。
// 普通函数
function Person(name){
  this.name = name
}
var p = new Person("小明");

// 模拟new过程
function _new(){
  // 1、创建一个新对象
  let obj = {};
  let [constructor, ...args] = [...arguments];  // 第一个参数是构造函数
  // 2、将新对象的 __proto__ 指向构造函数的 prototype
  obj.__proto__ = constructor.prototype;
  // 3、使用 apply,改变构造函数 this 的指向到新对象
  let result = constructor.apply(obj, args);
  if(result && (typeof result == "object" || typeof result == "function")){
    // 如果构造函数返回的结果是一个对象,就返回这个对象
    return result
  }
  // 如果构造函数返回的不是一个对象,就返回创建的新对象。
  return obj
}
let p2 = _new(Person, "小明")

6、继承
image

var A = function(){};
var a = new A();
console.log(a.__proto__); //A {}(即构造器function A 的原型对象)
console.log(a.__proto__.__proto__); //Object {}(即构造器function Object 的原型对象)
console.log(a.__proto__.__proto__.__proto__); //null

a.__proto__ == A.prototype
A.prototype.__proto__ == Object.prototype
A.prototype.constructor == A

A.__proto__ == Function.prototype
Object.__proto__ == Function.prototype
Function.__proto__ == Object.__proto__
Object.prototype.__proto__ == null

最常用的继承有以下四个:
1、原型继承 // 缺点:1、创建子类实例时,不能向父类的构造函数传递参数。2、子类实例共享了父类构造函数的引用属性(如:arr)。
2、组合继承 // 缺点:两次调用父类的构造函数,造成了不必要的消耗。
3、寄生组合继承
4、ES6 的 extend 继承

// 原型继承
var Son.prototype = Object.create(Father);

// 组合继承
function Father(name){
    this.name = name;
    this.colors = ["red","blue","green"];
}
function Son(name,age){
    Father.call(this,name);//第二次调用父类构造函数
    this.age = age;
}
Son.prototype = new Father();//第一次调用父类构造函数
Son.prototype.constructor = Son;

// 寄生组合继承
function Father(name){
    this.name = name;
    this.colors = ["red","blue","green"];
}
function Son(name,age){
    Father.call(this,name);
    this.age = age;
}
Son.prototype = Object.create(Father.prototype);
Son.prototype.constructor=Son;

二叉树的深度与广度遍历

如下,就是一个二叉树。

      1
    /    \
   2      3
  /  \   /  \
 4    5 6    7

1、从上到下遍历,称为深度遍历,深度遍历又有3种遍历策略,分别是前序遍历、中序遍历、后序遍历。
-前序遍历,遍历顺序是根左右:1245367
-中序遍历,遍历顺序是左根右:4251637
-后序遍历,遍历顺序是左右根:4526731

2、按层级遍历,则称为广度遍历,它的遍历顺序是:1234567

3、遍历代码实现:

// 前序遍历
 preorderTraversal = (tree) =>{
     //判断树是否为空
     if(tree == null) return false;
     //根节点
     console.log(tree.data)
     //左子树
     this.preorderTraversal(tree.left)
     //右子树
     this.preorderTraversal(tree.right)
}

// 中序遍历
inorderTraversal = (tree) =>{
    //判断树是否为空
    if(tree == null) return false;
    //左子树
    this.inorderTraversal(tree.left);
    //根节点
    console.log(tree.data)
    //右节点
    this.inorderTraversal(tree.right);
}

// 后序遍历
postorderTraversal = (tree) =>{
    //判断树是否为空
    if(tree == null) return false;
    //左子树
    this.postorderTraversal(tree.left);
    //右子树
    this.postorderTraversal(tree.right);
    //根节点
    console.log(tree.data)
}

// BFS广度遍历
function BFS(tree) {
    const queue = [] // 初始化队列queue
    // 根结点首先入队
    queue.push(tree)
    // 队列不为空,说明没有遍历完全
    while(queue.length) {
        const top = queue[0] // 取出队头元素  
        // 访问 top
        console.log(top.val)
        // 如果左子树存在,左子树入队
        if(top.left) {
            queue.push(top.left)
        }
        // 如果右子树存在,右子树入队
        if(top.right) {
            queue.push(top.right)
        }
        queue.shift() // 访问完毕,队头元素出队
    }
}

4、面试真题

翻转二叉树
输入:
     4
   /   \
  2     7
 / \   / \
1   3 6   9
输出:
     4
   /   \
  7     2
 / \   / \
9   6 3   1
/**
 * @param {TreeNode} root
 * @return {TreeNode}
 */
const invertTree = function(root) {
    // 定义递归边界
    if(!root) {
        return root;
    }
    // 递归交换右孩子的子结点
    let right = invertTree(root.right);
    // 递归交换左孩子的子结点
    let left = invertTree(root.left);
    // 交换当前遍历到的两个左右孩子结点
    root.left = right;
    root.right = left;
    return root;
};

JavaScript深浅拷贝的实现

1、我们先从一个问题说起,基本数据类型和引用数据类型有什么区别?

  1. 基本数据类型在被创建时,在栈上给其划分一块内存,将数值直接存储在栈上;
  2. 引用数据类型在被创建时,首先在栈上创建一个引用,而对象的具体内容都存储在堆内存上,然后由栈上面的引用指向堆中对象的地址。

2、基本数据类型和引用数据类型拷贝的时候有什么区别?

  1. 由于存储的位置不一样,直接拷贝的时候就会有两种情况:拷贝的值和拷贝的引用,也就是我们常说的深浅拷贝。
  2. 对于基本数据类型而言,没有深浅拷贝的概念,都是在栈上新开辟了一块内存给新的值。
  3. 对于引用数据类型而言,区别是会不会共享堆内存里的值。引用数据类型:Object,如果要细分的话有 Object、Array、Date、RegExp 和 Function。

3、如何实现一个深拷贝?

无非就是 JSON.stringify()递归两种方法。

4、通过JSON.stringify的方式实现深拷贝,会有什么问题?

如果值是 undefined、function 等,在转换的时候会丢失,如果存在循环引用,则会出错,所以还是要比较谨慎使用 JSON.stringify 来做深拷贝。

5、通过递归的方式实现深拷贝,会有什么问题?

存在循环引用的问题。问题如下:

  const a = {val:2};
  a.target = a; // 拷贝a会出现系统栈溢出,因为出现了无限递归的情况。

6、如何解决这个问题循环引用问题?

递归方法实现深度克隆原理:遍历对象、数组直到里边都是基本数据类型,然后再去复制。
循环引用,即对象的属性直接的引用了自身的情况,解决循环引用问题,我们可以额外开辟一个存储空间,来存储当前对象和拷贝对象的对应关系,当需要拷贝当前对象时,先去存储空间中找,有没有拷贝过这个对象,如果有的话直接返回,如果没有的话继续拷贝,这样就巧妙化解的循环引用的问题。

function deepClone(obj, map = new WeakMap()) {
  if (obj === null) return obj; // 如果是null或者undefined我就不进行拷贝操作
  if (obj instanceof Date) return new Date(obj);
  if (obj instanceof RegExp) return new RegExp(obj);
  if (typeof obj !== "object") return obj; // 可能是对象或者普通的值  如果是函数的话是不需要深拷贝 (typeof [1,2,3]输出"object")
  if (map.get(obj)) return map.get(obj); // 是对象的话就要进行深拷贝
  
  // new obj.constructor(),实例化一个同类型的空对象,如果是object则是{},如果是array则是[]
  let cloneObj = new obj.constructor(); // 找到的是所属类原型上的constructor,而原型上的 constructor指向的是当前类本身
  map.set(obj, cloneObj); // 保存这个空对象的引用
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      // 实现一个递归拷贝
      cloneObj[key] = deepClone(obj[key], map);
    }
  }
  return cloneObj;
}

let obj = { name: '阿猫', address: { x: 100 } };
obj.o = obj; // 对象存在循环引用的情况
obj.arr = [1, 2, 3];
obj.fn = function(){console.log('阿猫Function')};

let cloneObj = deepClone(obj);
cloneObj.address.x = 200;
cloneObj.arr = [4, 5, 6];
cloneObj.fn = function(){console.log('阿狗Function')};

console.log(obj.address.x); // 100
console.log(cloneObj.address.x); // 200
console.log(obj.arr); // [1, 2, 3]
console.log(cloneObj.arr); // [4, 5, 6]
console.log(obj.fn()); // 阿猫Function
console.log(cloneObj.fn()); // 阿狗Function

7、知识小扩展:实例化一个同类型的空对象

let a1 = {name: 'pan'};
let aa1 = new a1.constructor();
console.log(aa1, typeof aa1, Object.prototype.toString.call(aa1)); // {} "object" "[object Object]"

let a2 = [1,2,3,4,5];
let aa2 = new a2.constructor();
console.log(aa2, typeof aa2, Object.prototype.toString.call(aa2)); // [] "object" "[object Array]"

let a3 = new Set([1,2,3,4]);
let aa3 = new a3.constructor();
console.log(aa3, typeof aa3, Object.prototype.toString.call(aa3)); // Set(0) {} "object" "[object Set]"

栈的应用——JS实现括号匹配问题

实现一个isValid函数,校验([ ) ]、([{ )]、() [ ] { }等是否匹配

遇到这个问题的时候,我心中立马奔现出,以下这个简单粗暴的方法。。。

function isValid(str) {
  let a, b, c = 0;
  for (let s of str) {
    if (s == "(") a++;
    if (s == ")") a--;
    if (s == "[") b++;
    if (s == "]") b--;
    if (s == "{") c++;
    if (s == "}") c--;
  }
  return !(a || b || c);
}

后面经过自测,发现是有问题的,例如匹配这个"([ ) ]"字符串,得到的结果是true,这......还是乖乖用栈实现吧。

算法流程

  • 遍历字符串的每一个字符
  • 如果是左括号直接push入栈
  • 如果是右括号,将栈顶的第一个元素取出来与当前的元素进行对比,如果不匹配,则return false ,如果匹配,则出栈遍历完之后判断栈内是否为空;
function isValid(str) {
    // 以左右括号来建立一个对象,key为左括号,value为右括号
    let obj = {
      "{": "}",
      "(": ")",
      "[": "]",
    };
    //实例化一个栈
    let arr = [];
    //遍历str字符串
    for (let s of str) {
      if (obj[s]) {
        arr.push(s); //是左括号,入栈
      } else if (Object.values(obj).includes(s)) {
        // 右括号  将当前的元素和栈顶的第一个元素进行匹配
        let top = arr.pop();
        if (s !== obj[top]) return false;
      } else {
        //这里排除的是空字符的情况,如果不是左右括号而是其他的空字符串或者非法字符的话,将终止本次循环,执行下一次循环
        continue;
      }
    }
    //遍历完成之后要保证栈内要为空
    return arr.length === 0;
  }
  console.log(isValid("()")); //true
  console.log(isValid("([ ) ]")); //false
  console.log(isValid("([{ )]")); //false
  console.log(isValid("()[ ]{}")); //true
  console.log(isValid(" { ]")); //false
  console.log(isValid("{ [ ] }")); //true

总结:

  1. 其实这个题就是利用了栈的先进后出的特点,将栈作为一个容器。
  2. 事先声明一个可以建立左右括号对应关系的对象(key为左括号,value为右括号)。
  3. 使用for …of遍历字符串,如果遍历的元素是左括号,就直接push入栈,如果是右括号,将先前入栈的(即栈顶的第一个元素)取出来(pop 出栈)与当前遍历到的元素进行比对,如果不匹配则return false;如果匹配则出栈;如果左右括号都不是,那就终止本次循环(continue)。
  4. 最后判断传入的字符串是否合法等价于栈的长度是否为0。

工作当中常用到的CSS

1、上中下布局,margin-top方式

<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<title>内容不超过屏幕,footer固定在底部,超过时被撑出屏幕</title>
<style type="text/css">
* { 
    margin: 0;
    padding: 0; 
}

html, body { 
    height: 100%; 
}

.wrap {
    height: auto;
    min-height: 100%;
    text-align: center;
}

.main { 
    padding-bottom: 80px;
}

footer {
    height: 80px;
    line-height: 80px;
    margin-top: -80px;
    text-align: center;
}
</style>
</head>
<body>
<section class="wrap">
    <div class="main">
        <div>
            <h1>标题</h1>
            <p>我是内容。</p> 
            <p>是的,</p>
            <p>我真的是内容。</p> 
            <p>我不是内容?</p>
            <p>我是。</p>
            <br />
            <p>我没超过屏幕高度时,footer会老实待在固定在底部</p>
            <p>当我超过屏幕高度时,footer会被我撑出屏幕,但始终在我下面。</p>
            <p>是的,没错,就是这样。</p>
            <br />
        </div> 
    </div>
</section>

<footer>
    <h1>我是footer</h1>
</footer>

</body>
</html>

2、上中下布局,flex方式

<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<title>内容不超过屏幕,footer固定在底部,超过时被撑出屏幕</title>
<style type="text/css">
    * { 
        margin: 0;
        padding: 0; 
    }

    // 上中下布局(中部分自适应)
    .top_middle_bottom_wrap{
        position: fixed;
        height: 100%;
        width: 100%; 
        top: 0;
        left: 0;
        display: flex;
        flex-direction: column;
        min-height: 100%;
    }
    .top_middle_bottom_wrap .top_wrap{
        min-height: 45px;
    }
    .top_middle_bottom_wrap .middle_wrap{
        flex: 1;
    }
    .top_middle_bottom_wrap .bottom_wrap{
        min-height: 40px;
    }
</style>
</head>
<body>

    <div class="top_middle_bottom_wrap">
        <div class="top_wrap">
            <p>我是头部。</p> 
        </div>
        <div class="middle_wrap">
            <p>我是内容。</p> 
        </div>
        <div class="bottom_wrap">
            <p>我是底部。</p> 
        </div>
    </div>

</body>
</html>

3、flex从左往右并换行布局

第一种方式:
.img_wrap{
    display: flex;
    flex-wrap: wrap;
    .img_item{
      font-size: 0;
      flex: 0 0 108px;
      margin-left: 7px;
      margin-bottom: 7px;
      // 选择第一个元素或者换行的第一元素(3个元素为一行)
      &:nth-of-type(3n+1){
        margin-left: 0;
      }
    }
    img,
    video {
      width:108px;
      height:108px;
    }
    video {
      object-fit: contain;
    }
}
第二种方式:
.img_wrap{
    display: flex;
    flex-wrap: wrap;
    .img_item{
      font-size: 0;
      width: 25%;
      margin-bottom: 7px;
    }
    img,
    video {
      width:108px;
      height:108px;
    }
    video {
      object-fit: contain;
    }
}

4、并排的多个元素最多显示几行

<html>
<head>
    <meta charset="utf-8">
    <style>
        .tags {
            display: -webkit-box;
            -webkit-box-orient: vertical;
            -webkit-line-clamp: 3;
            overflow: hidden;
            text-overflow: ellipsis;
        }
        .tags > span {
            display: inline-block;
            padding: 5px;
            border:  1px solid #333;
            color: #333;
        }
    </style>
</head>

<body>
    <div class="tags">
        <span>标签1</span>
        <span>标签22222</span>
        <span>标签333333</span>
        <span>标签4</span>
        <span>标签555555</span>
        <span>标签666666</span>
        <span>标签77777777</span>
        <span>标签88888</span>
        <span>标签9999999999999999</span>
    </div>
</body>
</html>

5、左右上下箭头

<p>向右: <i class="arrows arrows_right"></i></p>
<p>向左: <i class="arrows arrows_left"></i></p>
<p>向上: <i class="arrows arrows_up"></i></p>
<p>向下: <i class="arrows arrows_down"></i></p>

.arrows {
  border: solid black;
  border-width: 0 1px 1px 0;
  display: inline-block;
  padding: 3px;
}

.arrows_right {
    transform: rotate(-45deg);
    -webkit-transform: rotate(-45deg);
}

.arrows_left {
    transform: rotate(135deg);
    -webkit-transform: rotate(135deg);
}

.arrows_up {
    transform: rotate(-135deg);
    -webkit-transform: rotate(-135deg);
    margin-top: 5px;
}

.arrows_down {
    transform: rotate(45deg);
    -webkit-transform: rotate(45deg);
    margin-bottom: 5px;
}

6、文字超出省略号(可指定宽度)

// 文字超出省略号,父元素是display:flex;,则父元素需要添加overflow: hidden;即可,用法:.text-overflow(200px)
.text-overflow(@max:100%) {
    max-width: @max;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

7、元素后面的垂直分隔线

.vertical_line{
    position: relative;
    margin-right: 24px;
    &::after{
        position: absolute;
        right: -16px;
        top: 50%;
        transform: translateY(-50%);
        content: '';
        width:0px;
        height:20px;
        border:1px solid rgba(201,201,201,1);
    }
    &:last-child{
        margin-right: 0;
        &::after{
            width:0;
            height:0;
            border:0;
            margin-right: 0;
        }
    }
}

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.