JavaScript - 杂记

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16

### 字符串

str为要去除空格的字符串:

  去除所有空格:   
    str = str.replace(/\s+/g,"");  
    
  去除两头空格:   
    str = str.replace(/^\s+|\s+$/g,"");
  
  去除左空格:
    str = str.replace( /^\s*/, '');
  
  去除右空格:
    str = str.replace(/(\s*$)/g, "");

使用多个字符分割字符串:

1
2
3
// 同时使用 ,_ 进行分割处理 
var mystring = "jb51.net,google.com,baidu.com_weibo.com_haotu.net";
var myarray = mystring.split(/[,_]/);

对象拼接

1
Object.assign({}, obj1, obj2)

客户端获取图片文件信息(宽高, 名字, 大小)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
export function getImageInfo (fileObj) {
  return new Promise((resolve, reject) => {
    var reader = new FileReader();
    reader.addEventListener('load', function() {
      var image = new Image();
      image.addEventListener('load', function() {
        resolve({
          name: fileObj.name,
          width: image.width,
          height: image.height,
          size: Math.round(fileObj.size/1024) + 'KB'
        })
      })
      image.src = window.URL.createObjectURL(fileObj);
    })
    reader.readAsDataURL(fileObj);
  }); 
}

一、div内显示一行,超出部分用省略号显示

1
2
3
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;

二、div内显示两行或三行,超出部分用省略号显示

1
2
3
4
5
    overflow: hidden;
    text-overflow: ellipsis;
    display: -webkit-box;
    -webkit-line-clamp: 2;(行数)
    -webkit-box-orient: vertical;

概述

-webkit-line-clamp 是一个 不规范的属性(unsupported WebKit property),它没有出现在 CSS 规范草案中。限制在一个块元素显示的文本的行数。 为了实现该效果,它需要组合其他外来的WebKit属性。常见结合属性:

display: -webkit-box; 必须结合的属性 ,将对象作为弹性伸缩盒子模型显示 。 -webkit-box-orient 必须结合的属性 ,设置或检索伸缩盒对象的子元素的排列方式 。 text-overflow 可以用来多行文本的情况下,用省略号“…”隐藏超出范围的文本 。

  • react 分辨浏览器种类
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// Opera 8.0+
var isOpera = (!!window.opr && !!opr.addons) || !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0;

// Firefox 1.0+
var isFirefox = typeof InstallTrigger !== 'undefined';

// Safari 3.0+ "[object HTMLElementConstructor]" 
var isSafari = /constructor/i.test(window.HTMLElement) || (function (p) { return p.toString() === "[object SafariRemoteNotification]"; })(!window['safari'] || (typeof safari !== 'undefined' && safari.pushNotification));

// Internet Explorer 6-11
var isIE = /*@cc_on!@*/false || !!document.documentMode;

// Edge 20+
var isEdge = !isIE && !!window.StyleMedia;

// Chrome 1+
var isChrome = !!window.chrome && !!window.chrome.webstore;

// Blink engine detection
var isBlink = (isChrome || isOpera) && !!window.CSS;

# 值传递&引用传递

结论: js 中什么类型是引用传递, 什么类型是值传递? 如何将值类型的变量以引用的方式传递?

JavaScript中没有引用传递,只有值传递。对象(引用类型)的传递只是拷贝一个新的引用,这个新的引用可以访问原本对象上的属性,但是这个新的引用本身是放在另外一个格子上的值(内存地址),直接往这个格子赋新的值,并不会影响原本的对象。

# 例子:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
  function changeAgeAndReference(person) {
    person.age = 25;
    person = {
        name: 'John',
        age: 50
    };
    
    return person;
    } 
  
  var personObj1 = {
      name: 'Alex',
      age: 30
  };
  var personObj2 = changeAgeAndReference(personObj1);
  console.log(personObj1); // -> ?
  console.log(personObj2); // -> ?

关键在于 person 对象和 personObj1 是两个对象, person 重新赋值前 他们指向的内存地址是相同的

1
2
{ name: 'Alex', age: 25 }
{ name: 'John', age: 50 }

# ES6 var 与 let 区别

# var 的使用会出现的问题

1. Js没有块级作用域, 在JS函数中的var声明,其作用域是函数体的全部
```
for(var i=0;i<10;i++){
    var a = 'a';
}
console.log(a);
```
明明已经跳出 for 循环了,却还可以访问到 for 循环内定义的变量 a ,甚至连 i 都可以被访问到,尴尬~

2. 循环内变量过度共享
```
for (var i = 0; i < 3; i++) {
  setTimeout(function () {
    console.log(i)
  }, 1000);
}
```
以上例子输出了3个3,而不是预想的 0、1、2
循环本身及三次 timeout 回调均共享唯一的变量 i 。当循环结束执行时,i 的值为3。所以当第一个 timeout 执行时,调用的 i 当然为 3 了。

# let是更完美的var

let声明的变量拥有块级作用域 也就是说用let声明的变量的作用域只是外层块,而不是整个外层函数。let声明仍然保留了提升特性,但不会盲目提升,在示例一中,通过将var替换为let可以快速修复问题,如果你处处使用let进行声明,就不会遇到类似的bug。

let声明的全局变量不是全局对象的属性 这就意味着,你不可以通过window.变量名的方式访问这些变量。它们只存在于一个不可见的块的作用域中,这个块理论上是Web页面中运行的所有JS代码的外层块。

形如for (let x…)的循环在每次迭代时都为x创建新的绑定。 这是一个非常微妙的区别,拿示例二来说,如果一个for(let…)循环执行多次并且循环保持了一个闭包,那么每个闭包将捕捉一个循环变量的不同值作为副本,而不是所有闭包都捕捉循环变量的同一个值。所以示例二中,也以通过将var替换为let修复bug。这种情况适用于现有的三种循环方式:for-of、for-in、以及传统的用分号分隔的类C循环。

用let重定义变量会抛出一个语法错误(SyntaxError)。 这个很好理解,用代码说话

1
2
let a = 'a';
let a = 'b';

上述写法是不允许的,浏览器会报错,因为重复定义了。

# const 定义常量

const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指针,const只能保证这个指针是固定的,至于它指向的数据结构是不是可变的,就完全不能控制了。因此,将一个对象声明为常量必须非常小心。

1
2
3
4
5
6
7
8
const foo = {};

//  foo 添加一个属性,可以成功
foo.prop = 123;
foo.prop // 123

//  foo 指向另一个对象,就会报错
foo = {}; // TypeError: "foo" is read-only

如果真的想将对象冻结,应该使用Object.freeze方法。

1
2
3
4
5
const foo = Object.freeze({});

// 常规模式时,下面一行不起作用;
// 严格模式时,该行会报错
foo.prop = 123;

除了将对象本身冻结,对象的属性也应该冻结。下面是一个将对象彻底冻结的函数。

1
2
3
4
5
6
7
8
var constantize = (obj) => {
  Object.freeze(obj);
  Object.keys(obj).forEach( (key, i) => {
    if ( typeof obj[key] === 'object' ) {
      constantize( obj[key] );
    }
  });
};
  • JavaScript 中不同类型以及不同环境下变量的内存都是何时释放?

    引用类型是在没有引用之后, 通过 v8 的 GC 自动回收, 值类型如果是处于闭包的情况下, 要等闭包没有引用才会被 GC 回收, 非闭包的情况下等待 v8 的新生代 (new space) 切换的时候回收.

# for

普通for循环没有什么特殊性

# for … in

for…in 用于循环对象的属性. 它可以是任何对象(Object). for in允许您访问对象的键, 但不提供对这些值的引用.在JavaScript对象属性本身有内部属性.其中一个内部属性是[[Enumerable]].因为只有在[Enumerbale]设置为true的情况下,才会穿过属性. 它不用于迭代集合的元素,而是用于迭代对象的属性.

# for … of

for…of 一个数据结构只要部署了Symbol.iterator属性,就被视为具有 iterator 接口,就可以用for…of循环遍历它的成员。也就是说,for…of循环内部调用的是数据结构的Symbol.iterator方法。for…of循环可以使用的范围包括数组、Set 和 Map 结构、某些类似数组的对象(比如arguments对象、DOM NodeList 对象)、后文的 Generator 对象,以及字符串。

Iterator 的遍历过程是这样的。

(1)创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。

(2)第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。

(3)第二次调用指针对象的next方法,指针就指向数据结构的第二个成员。

(4)不断调用指针对象的next方法,直到它指向数据结构的结束位置。

每一次调用next方法,都会返回数据结构的当前成员的信息。具体来说,就是返回一个包含value和done两个属性的对象。其中,value属性是当前成员的值,done属性是一个布尔值,表示遍历是否结束。

Iterator 接口的目的,就是为所有数据结构,提供了一种统一的访问机制,即for…of循环(详见下文)。当使用for…of循环遍历某种数据结构时,该循环会自动去寻找 Iterator 接口。

一种数据结构只要部署了 Iterator 接口,我们就称这种数据结构是“可遍历的”(iterable)。

ES6 规定,默认的 Iterator 接口部署在数据结构的Symbol.iterator属性,或者说,一个数据结构只要具有Symbol.iterator属性,就可以认为是“可遍历的”(iterable)。

ES6 的有些数据结构原生具备Iterator接口(比如数组)即不用任何处理,就可以被for…of循环遍历。原因在于,这些数据结构原生部署了Symbol.iterator属性(详见下文),另外一些数据结构没有(比如对象)。凡是部署了Symbol.iterator属性的数据结构,就称为部署了遍历器接口。调用这个接口,就会返回一个遍历器对象。

原生具备 Iterator 接口的数据结构如下。

  • Array
  • Map
  • Set
  • String
  • TypedArray
  • 函数的 arguments 对象
  • NodeList 对象

对象(Object)之所以没有默认部署Iterator接口,是因为对象的哪个属性先遍历,哪个属性后遍历是不确定的,需要开发者手动指定。本质上,遍历器是一种线性处理,对于任何非线性的数据结构,部署遍历器接口,就等于部署一种线性转换。不过,严格地说,对象部署遍历器接口并不是很必要,因为这时对象实际上被当作 Map 结构使用,ES5 没有 Map 结构,而 ES6 原生提供了。

1
2
3
4
5
6
7
8
9
  var arr = ['a', 'b', 'c', 'd'];
  
  for (let a in arr) {
    console.log(a); // 0 1 2 3
  }
  
  for (let a of arr) {
    console.log(a); // a b c d
  }

for…in循环读取键名,for…of循环读取键值

# forEach

forEach 用于遍历 数组 但是无法中途跳出forEach循环,break continue命令或return命令都不能奏效

# new

1
2
function Person (){}
const obj = new Person();
  1. 新建一个对象 obj
  2. 设置obj原型链指向Person对象的原型链
  3. 修改 this 指向自身 obj
  4. 判断返回值类型, 值类型返回 obj, 引用类型返回引用类型

代码过程:

1
2
3
4
5
6
7
8
function instantiate (fn, ...rests) {
  var obj = Object.create(fn.prototype);
  var val = fn.apply(obj, rests);
  return isPrimitive(val) ? obj : val;
}

function Person () {}
const obj = instantiate(Person);
Licensed under CC BY-NC-SA 4.0