沐鳴怎麼當代理?_Js數組拍平/扁平化的實現總匯

題目:

請寫出一個數組拍平函數。效果如下: 

var arr=['a', ['b', 'c'], 2, ['d', 'e', 'f'], 'g', 3, 4]; 
flat(arr) //a,b,c,2,d,e,f,g,3,4

 

方法一:使用toString方法先將arr轉換為一個字符串, 再以split分割為數組,再將數組裡面的元素轉換為数字類型

var arr =['a', ['b', 'c'], 2, ['d', 'e', 'f'], 'g', 3, 4];
function flat(arr) {
  return arr.toString().split(',').map(function(item){
       return Number(item)
  })
}
console.log(flat(arr))

方法二: toString 格式轉換 與方法一類似 都是隱士類型轉換  

var arr = ['a', ['b', 'c'], 2, ['d', 'e', 'f'], 'g', 3, 4];
// 方法二:toString(格式轉換)
var flag = function(arr) {
	let toString = Array.prototype.toString;
	Array.prototype.toString = function() {
		return this.join(',');
	};
	let result = arr + '';
	Array.prototype.toString = toString;
	return result;
};

console.log(flag(arr));

方法三: valueOf(格式轉換) 與方法一 二類似 都是隱士類型轉化原理  

// 方法三:valueOf(格式轉換)
Array.prototype.valueOf = function() {
	return this.join(',');
};

var flat = function(arr) {
	return arr + '';
};
console.log(flat(['a', ['b', 'c'], 2, ['d', 'e', 'f'], 'g', 3, 4]));

方法四: 利用reduce特性

function flat(arr) {
	return newArr = arr.reduce((a, b) => {
		return a.concat(b)
	}, [])
}
var arr = ['a', ['b', 'c'], '2', ['d', 'e', 'f'], 'g', 3, 4];
console.log(flat(arr));

方法五:利用遞歸  

function flat(array) {
	var result = [];
	var each = function(arr) {
		arr.forEach(item => {
			if (item instanceof Array) {
				each(item);
			} else {
				result.push(item);
			}
		});
	};
	each(array);
	return result.join(',');
}
var arr = ['a', ['b', 'c', [7, 8]], 2, ['d', 'e', 'f'], 'g', 3, 4];
console.log(flat(arr));

方法六: ES6的遍歷器 Iterator 給數據結構增加遍歷器必須增加一個next方法 

// Iterator
Array.prototype[Symbol.iterator] = function() {
	let arr = [].concat(this);
	// arr=['a', ['b', 'c'], '2', ['d', 'e', 'f'], 'g', 3, 4]
	let getFirst = function(array) {
		let first = array.shift();
		if (first instanceof Array) {
			if (first.length > 1) {
				arr = first.slice(1).concat(array);
			}
			first = first[0];
		}
		return first;
	};
	return {
		next: function() { //類似與遍歷
			let item = getFirst(arr);
			if (item) {
				return {
					value: item,
					done: false,
				};
			} else {
				return {
					done: true,
				};
			}
		},
	};
};
var flat = function(arr) {
	let r = [];
	for (let i of arr) {
		r.push(i);
	} // i 已經是單個元素
	return r.join(',');
};
var arr = ['a', ['b', 'c'], '2', ['d', 'e', 'f'], 'g', 3, 4];
console.log(flat(arr));

 

站長推薦

1.雲服務推薦: 國內主流雲服務商,各類雲產品的最新活動,優惠券領取。地址:阿里雲騰訊雲華為雲

2.廣告聯盟: 整理了目前主流的廣告聯盟平台,如果你有流量,可以作為參考選擇適合你的平台點擊進入

鏈接: http://www.fly63.com/article/detial/7423

沐鳴開戶_iframe父子傳參通信

在最近的項目裏面,用到了不少關於iframe父子傳參通信的相關操作,記錄一下,雖然很簡單,但是確實十分有用的;
iframe通信可以分為2種,跨域和非跨域兩種.分別說明;
有一點很重要,iframe是可以給name 屬性的;給上name 屬性可以省下一些代碼;

非跨域 父調子

//父頁面
<button class="b" id="b">點擊</button>
<iframe src="a.html" name='child' id="f"></iframe>

<script>
    var ob=document.getElementById('b');
    var msg='hellow,i'm your father!!'
    ob.onclick=function(){
        if(child.document.readyState=="complete"){
            child.window.fnChild(msg); //關鍵
        }
    }
</script>

//子頁面
<script>
function fnChild (arg) {
    console.log(arg); //確實得到了 hellow,i'm your father!!
}
</script>

父頁面調用子頁面使用 childFrameName.window.fnName();;當然有一點很重要,你需要判斷iframe 裏面的東西是否加載完成,如果加載未完成就調用的話是會報錯的;
判斷iframe 加載是否完成有2種方法
1,childFrameName.document.readyState==”complete”來判斷;
2,childFrameName.onload=function(){} 使用onload 回調函數,把調用的方法都寫在這個回調函數裏面;

非跨域 子調父

//在父頁面
<div id="a" class="a"></div>
<iframe src="a.html" name='child' id="f"></iframe>

<script>
    function changeColor(){
        var oDiv=document.getElementById('a');
        oDiv.style.backgroundColor="red";
    }
</script>

//在子頁面
<button class="ob" onclick="c()">anniu</button>
<script>
    function c(){
        parent.window.changeColor(); //關鍵
    }
</script>

同樣的,在子頁面調用父頁面的方法使用 parent.window.fnName()就可以了;

這種操作難免會遇到父頁面獲取子頁面的元素,或者子頁面獲取父頁面的元素進行操作;

非跨域 父頁面獲取子頁面元素操作

首先,我們有幾種方法拿到子頁面的window對象或者doucument 對象,(還是使用上面的html)

//原生js 獲取子頁面window對象
1, var childWindow=document.getElementById("f").contentWindow;
2, var childWindow=document.getElementsByTagName('f')[0].contentWindow;
//其實也就是普通的獲取方法,只是後面多了一個contentWindow;
//jquery
var childWindow=$('#f').contentWindow;

//獲取子頁面的document對象 (假設已經通過上面的方法得到了window對象)
var frameDoc=childWindow.document;
var frameBody=frameDoc.body;
//jquery 也是跟上面的一樣
var frameDoc=$(childWindow.document);

//原生獲取元素
childWindow.document.getElementById('a') //上面都已經拿到了子頁面的window對象,所以獲取子頁面的元素也就只需要想普通操作那樣獲取就好
childWindow.document.getElementById('a').style.color='red' //改個顏色

//jq拿子頁面元素
$('#f').contents().find('#a'); //$('#f').contents 這相當於拿到了iframe 裏面所有的dom;

非跨域 子頁面獲取父頁面元素

//原生js
window.parent.document.getElementById('a'); //window.parent獲取到父頁面的window對象,那麼接可以使用一般操作獲取元素
window.parent.document.getElementById('a').style.color="red";//dom操作
//jquery
$("#a",parent.document); //$(父頁面元素選擇器, parent.document);
$("#a",parent.document).css('border','1px solid red');

上面的是不存在跨域的情況,但是有時候會遇到跨域情況,在這次的項目裏面就是出於跨域狀態下,開始看了一些資料,說是在用一個iframe做中間層去做,但是太麻煩,在這裏介紹一個十分還用的方法postMessage


postMessage

window.postMessage() 方法可以安全地實現跨源通信。通常,對於兩個不同頁面的腳本,只有當執行它們的頁面位於具有相同的協議(通常為https),端口號(443為https的默認值),以及主機 (兩個頁面的模數 Document.domain設置為相同的值) 時,這兩個腳本才能相互通信。window.postMessage() 方法提供了一種受控機制來規避此限制,只要正確的使用,這種方法就很安全。

以上摘自MDN 原文 postMessage;

otherWindow.postMessage(message, targetOrigin, [transfer]);
//otherWindow 窗口對象
// message 傳遞的消息,可以是對象,可以是字符串
// target 目標窗口,* 代表所有;

postMessage十分強大,既可以子傳父,也可以父穿子,並且可以突破同源限制

來看我遇到的使用場景;
我在主頁面有個透明遮罩,裏面是一個iframe的登錄窗口,在子頁面點擊關閉的時候,需要關掉父頁面的這個登陸遮罩;在這裏存在跨域,所以使用上面的獲取元素,操作元素的方法不能夠使用,這裏使用postMassage來做

//子頁面
<div id="loginBox">登錄窗口</div>
<div id="close"></div>

//父頁面
<div id="loginMask">
    <iframe src="子頁面"></iframe>
</div>

//子頁面
<script>
    let oClose=document.getElementById('#close');
    oClose.onclick=function(){
        window.parent.postMessage('close','*');
    }
</script>
//父頁面
<script>
    window.addEventListener('message',function(event){
        if(event.data=='close'){
            let oLoginMask=document.getElementById('loginMask');
            oLoginMask.style.display="none";
        }
    })
</script>

上面的代碼其實很簡單,在子頁面裏面獲取了元素,該元素觸發點擊事件的時候,向父窗口發送一個消息,傳遞了一個消息;(這個消息參數會在接收頁面的event.data查到);
在父頁面監聽message事件,監聽到了就讓登錄遮罩消失;

父傳子

同樣,在父窗口也可以使用postMassage 來傳遞消息到子頁面;

//父頁面
<button id="btn">傳遞消息</button>
<iframe id='f' src="子頁面.html"></iframe>
//子頁面
<div id="a"></div>

//父頁面
<script>
    let oBtn=document.getElementById('btn');
    let oFrame=document.getElementById('f');
    oBtn.onclick=function(){
        oFrame.contentWindow.postMessage('hello','*');
    }
</script>

//子頁面
<script>
    window.addEventListener('message',function(){
        if(event.data=='hello'){
            document.getElementById('#a').innerText=event.data;
        }
    })
</script>

站長推薦

1.雲服務推薦: 國內主流雲服務商,各類雲產品的最新活動,優惠券領取。地址:阿里雲騰訊雲華為雲

2.廣告聯盟: 整理了目前主流的廣告聯盟平台,如果你有流量,可以作為參考選擇適合你的平台點擊進入

鏈接: http://www.fly63.com/article/detial/7419

沐鳴註冊平台官網_Js中new操作符的詳細過程

理解new對象過程,需要提前了解原型及原型鏈的相關知識
我們都知道,js當中創建對象使用的是原型設計模式,即使用new操作符調用構造函數
這裏先看一個例子:

function Person(name,sex){
    this.name = name;
    this.sex = sex;
};
Person.prototype.go = function(){
    return this.name;
} 
var p = new Person('金文','男');

這個過程可以分為4個步驟,具體如下:

①創建一個新的對象

var obj = {};

②把該對象的__proto__屬性設置為構造函數的prototype屬性,即完成原型鏈

obj.__proto__ = Person.prototype;

③執行構造函數中的代碼,構造函數中的this指向該對象(obj)

obj.name = '金文';
obj.sex = '男';
obj.go = function(){return '金文';}

④返回該對象obj;

var obj = {
    name:'金文',
    sex:'男',
    go:function(){
        return '金文';
    }
}

站長推薦

1.雲服務推薦: 國內主流雲服務商,各類雲產品的最新活動,優惠券領取。地址:阿里雲騰訊雲華為雲

2.廣告聯盟: 整理了目前主流的廣告聯盟平台,如果你有流量,可以作為參考選擇適合你的平台點擊進入

鏈接: http://www.fly63.com/article/detial/7418

沐鳴平台註冊登錄_JS数字精度

0.前言

最近在看計算機組成原理的浮點數部分,突然想起之前看過的一道快手面試題

為什麼js中0.1+0.2不等於0.3,應該如何解決?

這裏我們可以借這道題來說一下js的精度問題

1.JS數的儲存

二進制和浮點數和定點數

首先計算機裏面的數據肯定以二進制形式存儲
對於同一段二進制碼,不同的解讀方式肯定有不同的意義
對於小數,我們有定點數和浮點數兩種表示方法
目前計算機大多用浮點數,精度高,表示範圍大

一個數以浮點數二進制碼形式儲存,我們從二進制浮點數碼中能算出表達的二進制,然後二進制又可以得到相應的十進制,這就是他們的轉化關係

複習浮點數

我們複習一下計組中對浮點數的介紹 這裏以32位為例

如上圖,32位二進制碼中有三個部分,符號位,指數位,尾數位
浮點數計算公式:

從左往右看有三個部分,符號位指數位,尾數位
(1)(-1)^s表示符號位,當s=0,V為正數;當s=1,V為負數。
(2)2^E表示指數位。
(3)M表示有效数字,大於等於1,小於2。
這裡有兩個注意點,
1.M由於恆定為1.xxx,所以默認省略1
2.E不全為0或不全為1。這時E=E-127或者E=E-1023(64位)

我們按上面的規則算一下為什麼圖中0011111000100…00表示的0.15625
首先指數位置01111100表示的是124 則E=124-127=-3
然後我們看尾數,尾數位為1.01(加上了隱藏的1)
所以v=1.01*2的-3次方=0.00101
注意這個是二進制結果,轉化為十進制的就是2(-3次方)+2(-5次方)=0.125+0.125*0.25=0.15625

JS中數值

JavaScript 內部,所有数字都是以64位浮點數形式儲存,即使整數也是如此。所以,1與1.0是相同的,是同一個數。

我們看一下64位的JS数字是怎麼儲存的

JS中1和0.1的表示方法

在http://www.binaryconvert.com/這個網站上我們可以找到一個數的二進制浮點數表示
我們手動按上面的方法算一下,並且驗證看對不對

1的表示方法

1對應的二進制1.00000
那麼1對應的浮點數應該長成這樣
1.0* 2的0次方
指數為0 尾數為1.0 所以指數實際是1023 尾數是0000000000000…00

看來我們算的是對的

0.1的表示方法

0.1對應的二進制 0.000110011001100…(循環)
那麼1對應的浮點數應該長成這樣
1.10011001100….*2的-4次方
所以尾數位應該是10011001100….
指數應該是1019也就是01111111011

看來我們是對的

2.精度產生的原因

為什麼精度會產生呢

首先0.1這種轉化為二進制碼是有誤差的,尾數是一個不斷循環的數,明顯會有誤差

其次,在浮點數加法運算裏面,有對階操作。対階會損失一部分尾數,如果尾數後面都是0,沒影響。但是如果是0.1轉換成的二進制浮點數碼的尾數,対階的時候捨棄部分尾數明顯也會造成誤差。

如果一個大數和一個小數相加時,會產生很大的誤差,因為対階的時候尾數得截掉好多位

(1+0.1).toPrecision(20) //"1.1000000000000000888"
(100000000000+0.1).toPrecision(20)
"100000000000.10000610"

很明顯誤差大了

3.關於精度的額外知識點

0.1並不是0.1

上面的內容你如果理解了,你再看到0.1,你就會清楚,0.1並不是0.1,0.1的浮點數二進制碼是有誤差的,不可能算出0.1
我們看到的是瀏覽器幫我們做了處理的

不信,你可以試試

0.1.toPrecision(17)
// "0.10000000000000001"

JS安全數

面試喜歡考這個問題,啥叫安全數,就是在這個範圍數值都有一正一反,一一對應
尾數一共52位,加上一個隱藏位,共53位
也就是JS能表示的最大整數是2的53次方,這個數是16位
但是

Math.pow(2, 53) === Math.pow(2, 53) + 1 // true

實際上2的53次方都不安全了,所以是2的53次方-1
在es6中 是Number.MAX_SAFE_INTEGER

toPrecision vs toFixed

數據處理時,這兩個函數很容易混淆。它們的共同點是把数字轉成字符串供展示使用。注意在計算的中間過程不要使用,只用於最終結果。

不同點就需要注意一下:

  • toPrecision是處理精度,精度是從左至右第一個不為0的數開始數起。
  • toFixed是小數點后指定位數取整,從小數點開始數起。

兩者都能對多餘数字做湊整處理,也有些人用toFixed來做四舍五入,但一定要知道它是有 Bug 的。

如:1.005.toFixed(2)返回的是1.00而不是1.01。

原因:1.005實際對應的数字是1.00499999999999989,在四舍五入時全部被捨去!

怎麼解決這個問題呢,引入mathjs用它的round方法也可以,自己寫一套字符串邏輯去處理也可以


4.解決方案

誤差主要產生在進制轉化和浮點數運算的対階操作中
整數由於尾數後面全是0,同時轉化為二進制數沒有誤差,所以

我們第一種方案就是全部轉化為整數,計算完再轉化為小數類似這種

/**
 * 精確加法
 */
function add(num1, num2) {
  const num1Digits = (num1.toString().split('.')[1] || '').length;
  const num2Digits = (num2.toString().split('.')[1] || '').length;
  const baseNum = Math.pow(10, Math.max(num1Digits, num2Digits));
  return (num1 * baseNum + num2 * baseNum) / baseNum;
} 

我們第二種方案就是用現成的庫 mathjs之類的,原理就是不走浮點數那一套,轉化成字符串,自己實現運算邏輯,從性能上說肯定比原生慢一點

當然如果僅僅是展示類型的,3.0000000001保留兩位小數的這種還是tofixed(2)最快哦

站長推薦

1.雲服務推薦: 國內主流雲服務商,各類雲產品的最新活動,優惠券領取。地址:阿里雲騰訊雲華為雲

2.廣告聯盟: 整理了目前主流的廣告聯盟平台,如果你有流量,可以作為參考選擇適合你的平台點擊進入

鏈接: http://www.fly63.com/article/detial/7417

沐鳴平台網址_React 狀態管理的 3 個規則

react 組件內部的狀態是在渲染過程之間保持不變的封裝數據。 useState() 是 react hook,負責管理功能組件內部的狀態。

我喜歡 useState() ,它確實使狀態處理變得非常容易。但是我經常遇到類似的問題:

  • 我應該將組件的狀態劃分為小狀態,還是保持複合狀態?
  • 如果狀態管理變得複雜,我應該從組件中提取它嗎?該怎麼做?
  • 如果 useState() 的用法是如此簡單,那麼什麼時候需要 useReducer()?

本文介紹了 3 條簡單的規則,可以回答上述問題,並幫助你設計組件的狀態。

No.1 一個關注點

有效狀態管理的第一個規則是:

使狀態變量負責一個問題。

使狀態變量負責一個問題使其符合單一責任原則。

讓我們來看一個複合狀態的示例,即一種包含多個狀態值的狀態。

const [state, setState] = useState({
    on: true,
    count: 0
});

state.on    // => true
state.count // => 0

狀態由一個普通的 JavaScript 對象組成,該對象具有 on 和 count 屬性。

第一個屬性 state.on 包含一個布爾值,表示開關。同樣,`state.count 包含一個表示計數器的数字,例如,用戶單擊按鈕的次數。

然後,假設你要將計數器加1:

// Updating compound state
setUser({
    ...state,
    count: state.count + 1
});

你必須將整個狀態放在一起,才能僅更新 count。這是為了簡單地增加一個計數器而調用的一個大結構:這都是因為狀態變量負責兩個方面:開關和計數器。

解決方案是將複合狀態分為兩個原子狀態 on 和 count:

const [on, setOnOff] = useState(true);
const [count, setCount] = useState(0);

狀態變量 on 僅負責存儲開關狀態。同樣,count 變量僅負責計數器。

現在,讓我們嘗試更新計數器:

setCount(count + 1);
// or using a callback
setCount(count => count + 1);

count 狀態僅負責計數,很容易推斷,也很容易更新和讀取。

不必擔心調用多個 useState() 為每個關注點創建狀態變量。

但是請注意,如果你使用過多的 useState() 變量,則你的組件很有可能就違反了“單一職責原則”。只需將此類組件拆分為較小的組件即可。

No.2 提取複雜的狀態邏輯

將複雜的狀態邏輯提取到自定義 hook 中。

在組件內保留複雜的狀態操作是否有意義?

答案來自基本面(通常會發生這種情況)。

創建 React hook 是為了將組件與複雜狀態管理和副作用隔離開。因此,由於組件只應關注要渲染的元素和要附加的某些事件偵聽器,所以應該把複雜的狀態邏輯提取到自定義 hook 中。

考慮一個管理產品列表的組件。用戶可以添加新的產品名稱。約束是產品名稱必須是唯一的

第一次嘗試是將產品名稱列表的設置程序直接保留在組件內部:

function ProductsList() {
    const [names, setNames] = useState([]);  
    const [newName, setNewName] = useState('');

    const map = name => <div>{name}</div>;

    const handleChange = event => setNewName(event.target.value);
    const handleAdd = () => {    
        const s = new Set([...names, newName]);    
        setNames([...s]);  };
    return (
        <div className="products">
            {names.map(map)}
            <input type="text" onChange={handleChange} />
            <button onClick={handleAdd}>Add</button>
        </div>
    );
}

names 狀態變量保存產品名稱。單擊 Add 按鈕時,將調用 addNewProduct() 事件處理程序。

在 addNewProduct() 內部,用 Set 對象來保持產品名稱唯一。組件是否應該關注這個實現細節?不需要。

最好將複雜的狀態設置器邏輯隔離到一個自定義 hook 中。開始做吧。

新的自定義鈎子 useUnique() 可使每個項目保持唯一性:

// useUnique.js
export function useUnique(initial) {
    const [items, setItems] = useState(initial);
    const add = newItem => {
        const uniqueItems = [...new Set([...items, newItem])];
        setItems(uniqueItems);
    };
    return [items, add];
};

將自定義狀態管理提取到一個 hook 中后,ProductsList 組件將變得更加輕巧:

import { useUnique } from './useUnique';

function ProductsList() {
  const [names, add] = useUnique([]);  const [newName, setNewName] = useState('');

  const map = name => <div>{name}</div>;

  const handleChange = event => setNewName(e.target.value);
  const handleAdd = () => add(newName);
  return (
    <div className="products">
      {names.map(map)}
      <input type="text" onChange={handleChange} />
      <button onClick={handleAdd}>Add</button>
    </div>
  );
}

const [names, addName] = useUnique([]) 啟用自定義 hook。該組件不再被複雜的狀態管理所困擾。

如果你想在列表中添加新名稱,則只需調用 add(‘New Product Name’) 即可。

最重要的是,將複雜的狀態管理提取到自定義 hooks 中的好處是:

  • 該組件不再包含狀態管理的詳細信息
  • 自定義 hook 可以重複使用
  • 自定義 hook 可輕鬆進行隔離測試

No.3 提取多個狀態操作

將多個狀態操作提取到化簡器中。

繼續用 ProductsList 的例子,讓我們引入“delete”操作,該操作將從列表中刪除產品名稱。

現在,你必須為 2 個操作編碼:添加和刪除產品。處理這些操作,就可以創建一個簡化器並使組件擺脫狀態管理邏輯。

同樣,此方法符合 hook 的思路:從組件中提取複雜的狀態管理。

以下是添加和刪除產品的 reducer 的一種實現:

function uniqueReducer(state, action) {
    switch (action.type) {
        case 'add':
            return [...new Set([...state, action.name])];
        case 'delete':
            return state.filter(name => name === action.name);
        default:
            throw new Error();
    }
}

然後,可以通過調用 React 的 useReducer() hook 在產品列表中使用 uniqueReducer():

function ProductsList() {
    const [names, dispatch] = useReducer(uniqueReducer, []);
    const [newName, setNewName] = useState('');

    const handleChange = event => setNewName(event.target.value);

    const handleAdd = () => dispatch({ type: 'add', name: newName });
    const map = name => {
        const delete = () => dispatch({ type: 'delete', name });    
        return (
            <div>
                {name}
                <button onClick={delete}>Delete</button>
            </div>
        );
    }

    return (
        <div className="products">
            {names.map(map)}
            <input type="text" onChange={handleChange} />
            <button onClick={handleAdd}>Add</button>
        </div>
    );
}

const [names, dispatch] = useReducer(uniqueReducer, []) 啟用 uniqueReducer。 names 是保存產品名稱的狀態變量,而 dispatch 是使用操作對象調用的函數。

當單擊 Add 按鈕時,處理程序將調用 dispatch({ type: ‘add’, name: newName })。調度一個 add 動作使 reducer uniqueReducer 向狀態添加一個新的產品名稱。

以同樣的方式,當單擊 Delete 按鈕時,處理程序將調用 dispatch({ type: ‘delete’, name })。remove 操作將產品名稱從名稱狀態中刪除。

有趣的是,reducer 是命令模式的特例。

總結

狀態變量應只關注一個點。

如果狀態具有複雜的更新邏輯,則將該邏輯從組件提取到自定義 hook 中。

同樣,如果狀態需要多個操作,請用 reducer 合併這些操作。

無論你使用什麼規則,狀態都應該盡可能地簡單和分離。組件不應被狀態更新的細節所困擾:它們應該是自定義 hook 或化簡器的一部分。

這 3 個簡單的規則能夠使你的狀態邏輯易於理解、維護和測試。

作者:Dmitri Pavlutin

翻譯:瘋狂的技術宅

原文:https://dmitripavlutin.com/

站長推薦

1.雲服務推薦: 國內主流雲服務商,各類雲產品的最新活動,優惠券領取。地址:阿里雲騰訊雲華為雲

2.廣告聯盟: 整理了目前主流的廣告聯盟平台,如果你有流量,可以作為參考選擇適合你的平台點擊進入

鏈接: http://www.fly63.com/article/detial/8142

沐鳴登錄測速_socket.io的接入使用

WebSocket 簡介

傳統的客戶端和服務器通信協議是HTTP:客戶端發起請求,服務端進行響應,服務端從不主動勾搭客戶端。

這種模式有個明顯軟肋,就是同步狀態。而實際應用中有大量需要客戶端和服務器實時同步狀態的場景,比如聊天室、股票行情、在線共享文檔等都需要客戶端實時拿到服務器的最新狀態。

針對這種實時同步的需求,一種簡單的方式是輪詢,比如每隔5s發一次http請求去拿服務器最新的狀態數據。但這種方式會存在數據延遲,浪費帶寬等副作用。

更完美的方式是使用WebSocket,瀏覽器原生支持,W3C標準協議,客戶端和服務器建立持久性連接可以互發消息。

socket.io 簡介

socket.io 是一個類庫,內部封裝了WebSocket,可以在瀏覽器與服務器之間建立實時通信。

如果某些舊版本的瀏覽器不支持WebSocket,socket.io會使用輪詢代替。另外它還具有可發送二進制消息、多路復用、創建房間等特性,因此相比直接使用原生WebSocket,socket.io是更好的選擇。

開發一個實時應用主要分兩部分:服務端和客戶端,socket.io分別提供了相應的npm包供我們方便地調用。

接下來就通過一個生動形象且有趣的栗子分別介紹這兩大塊。

現在假設李白,瑤,呂布,后羿,貂蟬5個人加入了一個叫 KPL 的房間,在文章結束時我們將擁有一個麻雀雖小五臟俱全的峽谷英雄在線聊天室

服務端api

首先安裝socket.io提供的服務端npm包:

npm i socket.io

可以與 Express 框架配合使用:

const http = require('http')
const app = require('express')()
const server = http.createServer(app)
const io = require('socket.io')(server)
server.listen(3000)

也可以與 Koa 框架配合使用

const http = require('http')
const Koa = require('koa')
const app = new Koa()
const server = http.createServer(app.callback())
const io = require('socket.io')(server)
server.listen(3000)

使用起來就是這麼簡單。接下來就可以寫業務邏輯啦

io.on('connect', client => { // client 即是連接上來的一個客戶端
  console.log(client.id) // id 是區分客戶端的唯一標識

  client.on('disconnect', () => {}) // 客戶端斷開連接時調用(可能是關掉頁面,網絡不通了等)
})

connect 和 disconnect 是 socket.io 內置的事件類型,用於在客戶端連接和斷開的時候做一些事情。

在客戶端建立連接時需要把他們加入到一個房間里去,類似創建了一個聊天室

  console.log(client.id)
+ client.join('KPL') // 將客戶端加入到 KPL 房間內
  client.on('disconnect', () => {})

緊接着瑤進來秒發了首條消息:我打野,不給就送

服務器在收到這條振奮人心的消息后需要立即同步給其他四位隊友

  client.join('KPL')
+ client.on('talk', message => {
+   client.to('KPL').emit('talk', message) // 發送給房間里的每個人,除了發送者
+ })
  client.on('disconnect', () => {})

服務端的功能到這基本上就開發完了。創建了一個房間,並在收到成員消息時立即同步給房間里的其他成員

客戶端api

socket.io 為客戶端提供了另一個npm包,直接安裝

npm i socket.io-client

接下來就可以在頁面上建立到服務器的連接啦

import io from 'socket.io-client'

const socket = io() // 建立連接

向服務器發送消息

  const socket = io()
+ socket.emit('talk', '我打野,不給就送')

接收服務器發來的消息

  const socket = io()
+ socket.on('talk', message => {
+ })

李白看到了瑤的消息,強忍住問候對方家人的衝動,像哄那啥似地說道:

  socket.on('talk', message => {
+   socket.emit('talk', '你買個石頭騎在我頭上他不香么')
  })

客戶端的功能到這基本上也開發完了。核心api就是on和emit用於收發消息,既簡單又優雅。

最後

至此一個可以實時發送接收消息的聊天室就完成了,雖然簡陋,但核心功能完備。

瑤最終倔強地打了野,李白選擇了上路,3分鐘被對面捶到高地,后羿在家裡等鳥,呂布和貂蟬躲在藍buff旁邊的草叢里聊天,就這樣在李白和瑤互相拉票舉報對方的全局消息中遊戲結束

站長推薦

1.雲服務推薦: 國內主流雲服務商,各類雲產品的最新活動,優惠券領取。地址:阿里雲騰訊雲華為雲

2.廣告聯盟: 整理了目前主流的廣告聯盟平台,如果你有流量,可以作為參考選擇適合你的平台點擊進入

鏈接: http://www.fly63.com/article/detial/7409

沐鳴首頁_碼農如何通過編程賺更多的錢

有很多機會可以將收入擴展到日常工作之外

成為程序員是一件很棒的事。 不僅大多數時候工作都很有趣,而且周圍有很多工作機會,而且大多數人的薪水都很高。

但是有時候,多花一些錢是值得讚賞的。 是因為您仍在上大學,還是想開始為自己而不是別人工作,您有一個孩子並想花更多的時間陪伴他們,您仍然需要賺錢或在做開發,就是圖個好玩兒。

這是一件好事:作為程序員,您擁有增加現金流量所需的一切。 您的大腦,您的筆記本電腦-這才是您真正需要的。 有興趣嗎?查看以下策略,並確定最適合您的策略。

開始自由職業

自由職業可能是一件好事。 沒有(真正的)老闆,可供選擇的項目很多,專家的日租金高,您想要的假期多……列表還在繼續。

但是,找到客戶和項目需要大量的紀律和精力。 對我來說,比較大的好處是您可以在晚上或周末在您的永久性工作旁邊開始自由職業。

像Upwork或Fiverr這樣的平台似乎提供了很多機會,尤其是在場邊做事,但要意識到那裡的競爭。

此外,費率非常低,因此,如果您只是想第一次將腳趾伸入水中,或者僅對少量額外收入感到滿意,我將不建議您這樣做。

更好的策略是使用您的LinkedIn個人資料,聯繫招聘人員和網絡中的過往客戶,參加會議和聚會,並尋找與遠程工作人員與公司相匹配的平台。

參加編程比賽

是的,這是真實的東西。 有專門的平台可以組織編程競賽,以獲得真正的獎金。

比較大的公司之一是擁有超過一百萬會員和眾多比賽的Topcoder。 它們具有三個主要重點領域:設計,數據科學和開發。

您將從事由2000多家公司發起的實際項目或與對手的一場比賽。 保證樂趣,快速學習也是如此。

如果您喜歡挑戰,這可能適合您。 但是,存在競爭,因此您不能指望穩定的收入流,因此請優先考慮這一點。

開始寫作

我認為,寫作仍然是吸引廣泛受眾的優秀方法之一。 您有很多機會開始寫作並從中賺錢:

  • 您可以創建自己的博客並通過廣告收入獲利。
  • 您可以編寫書籍或电子書,然後在線銷售。
  • 您可以在頭條等平台上撰寫文章,並參与其合作夥伴計劃。
  • 您可以為已建立的網站(如css-Tricks)撰寫來客帖子,如果您的文章被接受,則將向您支付固定金額。

嘗試並觀察人們對您所寫內容的反應並沒有錯。

但是,您應該考慮的事情是選擇對自己有特殊興趣的利基市場(保持积極性),保持一致的寫作(需要時間來獲得認可),並不斷提高寫作技巧以實現更高質量的文章(人們會感謝您,那裡有足夠多的低質量文章……)。

錄製和出售在線課程

在我看來,能夠在網上教人是過去十年來比較好的事情之一,而且至少可以持續十年。

對學生和老師的好處都是巨大的。 學生可以從多種產品中進行選擇,並按自己的時間表學習。 教師的內容可以觸及1000或100,000的人。

如果您具有編程經驗,最好是具有JavaScript或Python(或任何其他流行語言或框架)之類的語言的專業知識,甚至是利基滲透測試,並且您能夠並且樂於教別人,那麼創建在線課程可能就是您的事 。

有許多平台可用於發布您的課程。 例如,Udemy每月大約有7500萬遊客,任何人都可以加入。

Udemy traffic overview

其他平台(例如Frontend Masters或Pluralsight)僅應邀參加,但如果您有聲譽或良好的網絡,為什麼呢?

但是,錄製課程時需要注意以下幾點:

  • 購買優質裝備:必備麥克風和網絡攝像頭!
  • 高質量的內容為王。 競爭在穩步增長,因此您需要說服人們您可以教給他們有價值的東西。
  • 練習大聲而清晰地講話。
  • 務必重做您的錄音。
  • 創建其他材料,例如GitHub項目,演示文稿,編碼示例…

即使一門課程被記錄下來並且人們開始購買它似乎很吸引人,它也會為您創造被動收入,但這在一定程度上是正確的。 比較好的老師會不斷更新他們的課程,因為技術一直在變化!

開始成為技術主播

許多人不喜歡它在鏡頭前,但仍然是該領域的專家,並希望與他人分享他們的知識。

播客無疑是一個很好的媒介。 儘管他們已經存在了一段時間,但在過去的四到五年中,它們已經大受歡迎,吸引了成千上萬的人每天上下班或上床睡覺。

現在,大多數播客都可以自由收聽,因此金錢更多是一種副作用。 許多播客的節目贊助商都是用服務或金錢付費的。 許多播客都在Patreon上,該平台上的人們可以按月向他們付款以支持他們的工作並解鎖專有內容。

但是,為了成功播客,您應該能夠很好地表達自己的觀點,購買一些好的設備(麥克風),最重要的是,要有耐心不斷錄製情節。

人們必須先製作一兩年的每周情節,然後才能真正看到聽眾的進步,這種情況並不少見。

因此,如果您在執行此操作之前真的對此充滿熱情,那就太好了。

結論

我要告訴您的最後一件事與上述每個選項有關:

一致性為王。

無論您從什麼開始,都必須堅持下去。 大多數事情不會在一夜之間發揮作用。 這是艱苦的工作,您必須花費時間和精力。 99%的人放棄得太早。 躋身成功的1%之列!

原文 http://news.51cto.com/art/202002/609781.htm

站長推薦

1.雲服務推薦: 國內主流雲服務商,各類雲產品的最新活動,優惠券領取。地址:阿里雲騰訊雲華為雲

2.廣告聯盟: 整理了目前主流的廣告聯盟平台,如果你有流量,可以作為參考選擇適合你的平台點擊進入

鏈接: http://www.fly63.com/article/detial/7406

沐鳴:_油猴腳本編寫教程

油猴腳本(Tampermonkey)是一個非常流行的瀏覽器擴展,它可以運行由廣大社區編寫的擴展腳本,來實現各式各樣的功能,常見的去廣告、修改樣式文件、甚至是下載視頻。今天我們就來看看如何編寫自己的油猴腳本。當然為了運行油猴腳本,你應該在瀏覽器中安裝油猴插件。

安裝油猴插件

安裝油猴插件非常簡單,直接在瀏覽器的擴展商店中安裝即可。國產瀏覽器的話一般可以通過下載擴展文件手動拖動的方式來安裝。下圖是微軟新版Edge瀏覽器的擴展商店,直接搜索Tampermonkey即可。

新建腳本

首先在瀏覽器右上角找到並點擊油猴插件,選擇添加新腳本。

然後就會打開如圖所示的編輯器窗口,我們就可以在其中編輯自己的腳本文件了。如果你喜歡的話,還可以將腳本內容複製到合適的編輯器中編輯,完成之後再複製回來。

如果你點擊開發者菜單的話,可以選擇ES6模板,然後就可以在腳本中使用新版JavaScript的特性了,它會有Babel轉譯回ES5。不過這個模板貌似有點問題,用了它就沒辦法使用代碼糾錯功能了。所以這裏我還是選擇了默認的ES5模板。

腳本編寫方法

功能註釋

首先來看看腳本的內容,上面是一大排註釋,這些註釋可以非常有用的,它表明了腳本的各個屬性。下面來簡單介紹一下。

屬性名 作用
name 油猴腳本的名字
namespace 命名空間,類似於Java的包名,用來區分相同名稱的腳本,一般寫成作者名字或者網址就可以了
version 腳本版本,油猴腳本的更新會讀取這個版本號
description 描述,用來告訴用戶這個腳本是干什麼用的
author 作者名字
match 只有匹配的網址才會執行對應的腳本,例如*、http://*、http://www.baidu.com/*等,參見谷歌開發者文檔
grant 指定腳本運行所需權限,如果腳本擁有相應的權限,就可以調用油猴擴展提供的API與瀏覽器進行交互。如果設置為none的話,則不使用沙箱環境,腳本會直接運行在網頁的環境中,這時候無法使用大部分油猴擴展的API。如果不指定的話,油猴會默認添加幾個最常用的API
require 如果腳本依賴其他js庫的話,可以使用require指令,在運行腳本之前先加載其他庫,常見用法是加載jquery
connect 當用戶使用GM_xmlhttpRequest請求遠程數據的時候,需要使用connect指定允許訪問的域名,支持域名、子域名、IP地址以及*通配符
updateURL 腳本更新網址,當油猴擴展檢查更新的時候,會嘗試從這個網址下載腳本,然後比對版本號確認是否更新

腳本權限

下面簡單介紹一下grant指令那裡可以填寫的一些權限,詳情請查看油猴腳本文檔。這裏就簡單介紹幾個常用的,可以調用的函數全部以GM_作為開頭。

權限名 功能
unsafeWindow 允許腳本可以完整訪問原始頁面,包括原始頁面的腳本和變量。
GM_getValue(name,defaultValue) 從油猴擴展的存儲中訪問數據。可以設置默認值,在沒成功獲取到數據的時候當做初始值。如果保存的是日期等類型的話,取出來的數據會變成文本,需要自己轉換一下。
GM_setValue(name,value) 將數據保存到存儲中
GM_xmlhttpRequest(details) 異步訪問網頁數據的API,這個方法比較複雜,有大量參數和回調,詳情請參考官方文檔。
GM_setClipboard(data, info) 將數據複製到剪貼板中,第一個參數是要複製的數據,第二個參數是MIME類型,用於指定複製的數據類型。
GM_log(message) 將日誌打印到控制台中,可以使用F12開發者工具查看。
GM_addStyle(css) 像網頁中添加自己的樣式表。
GM_notification(details, ondone), GM_notification(text, title, image, onclick) 設置網頁通知,請參考文檔獲取用法。
GM_openInTab(url, loadInBackground) 在瀏覽器中打開網頁,可以設置是否在後台打開等幾個選項

還有一些API沒有介紹,請大家直接查看官方文檔吧。

編寫腳本

編寫腳本就很簡單了,編寫到// Your code here ..那裡即可。可以編寫函數,然後在最後調用這幾個函數,這樣的模塊化編寫方法寫出來的腳本比較容易維護。

等vagrant更新時候提醒我的腳本

前段時間了解了vagrant這個東西,感覺很有意思,準備研究一下,但是照着官網教程運行的時候,第一步就發生了錯誤。我上網一搜,原來我更新的virtualbox比較新,vagrant恰好不支持。但是如今幾個月過去了,vagrant還是沒有更新,所以我要寫一個腳本,等到vagrant更新的時候,給我網頁上彈出一個對話框。

首先訪問vagrant官網,然後就可以看到中間下載按鈕上大大的版本號2.2.6了。因為版本肯定是不會倒退的,所以只要判斷一下版本號不是2.2.6,就可以彈出提示了。通過F12開發者工具可以看到,這三個按鈕其實都是鏈接,只不過显示成了按鈕的樣子,而且他們恰好都位於header標籤之中。如果如果可以的話,直接用選擇器就可以非常輕鬆的獲取到版本號。

為了能在更新的時候及時獲取到提示,我需要腳本在所有網站上生效,來檢測版本。但是這樣做會導致另外一個問題,那就是每次打開一個網頁都會運行一次檢查vagrant的腳本,而這是完全不必要的。所以需要一個額外的判斷,這就需要利用油猴提供的API來保存當前日期,只有每天第一次的時候才會執行檢查代碼。本來我想的很複雜,需要一個日期變量,然後還要額外一個變量保存是否是今天第一次更新。後來我發現我想的太多了,做法其實很簡單。每天先獲取一次日期,然後和事先保存的日期比較,如果不一樣的話才執行腳本,並將日期設置為今天的日期;如果日期一樣的話無事發生。

最後一個問題就是如何來判斷版本號,有兩種方法:第一種就是上面提到的,直接解析html代碼並找到版本號;第二種是更直接的辦法, 因為vagrant也是Github上開源的項目,所以可以直接調用Github的API來獲取最新發布的版本號。可惜的是,第二種辦法我試了一下居然不成功,不知為何,沒辦法獲取到發布信息,但是換成其他項目就可以。所以最後沒辦法只好採用第一種辦法。有興趣的同學可以自己試一下第二種方法。

好了,所有相關的坑我都已經解釋完畢了,相信大家應該很容易就可以看懂下面的代碼,我就不介紹了。雖然看着簡單,但是我其實還是踩了不少的坑,就這點代碼花了我好幾天的時間。而且確實這個代碼寫的也並不是很好,因為ajax取回來的代碼是完整一個html頁面,貌似用原版DOM API沒辦法解析,最後只好用jQuery的parseHTML方法解析的。而且我還因為原生方法和jQuery之間的方法名搞混了,浪費了很多時間。

// ==UserScript==
// @name         remind_me_vagrant_update
// @namespace    https://github.com/techstay/myscripts
// @version      0.1
// @description  remind me if vagrant support virtualbox
// @author       techstay
// @match        *
// @require      https://cdn.staticfile.org/jquery/3.4.1/jquery.min.js
// @connect vagrantup.com
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_setClipboard
// @grant GM_log
// @grant GM_xmlhttpRequest
// @grant unsafeWindow
// @grant window.close
// @grant window.focus
// ==/UserScript==

(function () {
    'use strict';

    const CHECKED_DATE = 'checkedDate';

    function checkDateEquals(a, b) {
        return a.getFullYear() === b.getFullYear() &&
            a.getMonth() === b.getMonth() &&
            a.getDate() === b.getDate();
    }

    function checkVagrantVersion() {
        GM_setValue(CHECKED_DATE, new Date());
        GM_xmlhttpRequest({
            "method": "GET",
            "url": "https://www.vagrantup.com/",
            "headers": {
                "user-agent": 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36'
            },
            "onload": function (result) {
                var list = jQuery.parseHTML(result.response);
                jQuery.each(list, function (i, el) {
                    if (el.nodeName == 'HEADER') {
                        var header = jQuery.parseXML(el.innerHTML);
                        var version = header.getElementsByTagName('a')[1].textContent.replace('Download ', '');
                        if (version != '2.2.6') {
                            alert('Vagrant update!');
                        }
                        return false;
                    }
                });
            }
        });
    }

    var today = new Date();
    var lastCheckedDay = new Date(GM_getValue(CHECKED_DATE, new Date('2006-1-1')));
    if (!checkDateEquals(lastCheckedDay, today)) {
        checkVagrantVersion();
    }

})();

調試腳本

編寫腳本很難一次成功,大部分時間都花在了調試上面。調試油猴腳本的話有幾種調試方法。

第一種方法就是最原始的打印日誌,可以利用console.log和GM_log來將關鍵信息打印出來,上面的腳本就是我靠打印日誌一點點發現各種參數錯誤的。說實話這種辦法有點笨。

第二種就是利用瀏覽器的調試功能,在腳本需要調試的地方插入debugger;語句,然後在打開F12開發者工具的情況下刷新頁面,就會發現網頁已經暫停在相應位置上。這樣就可以利用F12開發者工具進行單步調試、監視變量等操作了。

將文章同步複製到Csdn和思否編輯器的腳本

我的文章一般都是簡書首發,然後複製粘貼到Csdn中,但是後來我發現每次手動操作太蠢了,為什麼不用腳本來自動化呢?所以我又寫了個腳本幫忙完成自動化工作。本來以為這個腳本應該比較簡單,不過還是踩了很多坑才湊合把功能寫出來。

首先是數據的保存,利用油猴提供的GM_setValue倒是可以很簡單的將文章標題和內容保存起來。不過問題來了,如何在不同頁面之間共享呢?有幾種方案:第一種最簡單粗暴,直接複製兩份,對應頁面首先判斷是否存在數據,存在的話才執行複製操作,然後清空數據。這種方案最簡單,而且如果自己直接新建文章的話也不會出問題。第二種就是數據只保存一份,通過幾個變量來確定什麼時候複製完成,清空數據,但是這樣比較複雜,要理清邏輯順序很麻煩。所以最後我就採用了第一種辦法。

然後又遇到一個問題,那就是如果編輯器自帶了保存和恢復功能,很可能會把我複製過去的文章給覆蓋了,所以需要等頁面加載完之後,延遲一段時間才進行複製操作。然後我又谷歌了一番,差不多解決了這個問題。

然後遇到了一個非常棘手的問題,就是SF的編輯器設計比較複雜,沒辦法通過直接填充value或者text屬性的方式來寫入文章,我想了很久也沒有想出來怎麼解決。沒辦法只好改用剪貼板的方式來糊弄了,也就是將文章內容複製到剪貼板裡頭,然後手動粘貼到編輯器中。

最後一個問題就是簡書上這個複製按鈕應該如何實現,其實簡書編輯器的工具欄倒是空了一些部分,我本來想把按鈕直接加到那個上面。但是我發現貌似一旦添加東西,那個工具欄會自動重載取消更改,所以水平所限沒做到,只好利用jQueryUI加了一個很醜的浮動按鈕,而且因為拖動的時候會觸發單擊,沒辦法把按鈕改成了雙擊觸發。

最後的腳本就是下面這樣的。相比第一個腳本多了幾個打開新頁面、刪除變量、訪問剪貼板的API。

// ==UserScript==
// @name         copy_jianshu_to_csdn_and_segmentfault
// @namespace    https://github.com/techstay/myscripts
// @version      0.1
// @description  將簡書文章複製到csdn和思否編輯器中
// @author       techstay
// @match        https://editor.csdn.net/md/
// @match https://segmentfault.com/write
// @match https://www.jianshu.com/writer*
// @require      https://cdn.staticfile.org/jquery/3.4.1/jquery.min.js
// @require      https://cdn.bootcss.com/jqueryui/1.12.1/jquery-ui.min.js
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_deleteValue
// @grant unsafeWindow
// @grant GM_setClipboard
// @grant window.close
// @grant window.focus
// @grant GM_openInTab
// ==/UserScript==
(function () {
    'use strict';

    const SF_URL = 'https://segmentfault.com/write'
    const CSDN_URL = 'https://editor.csdn.net/md/'

    const SF_TITLE = 'sf_title'
    const SF_CONTENT = 'sf_content'
    const CSDN_TITLE = 'csdn_title'
    const CSDN_CONTENT = 'csdn_content'

    function saveArticle() {
        GM_setValue(CSDN_TITLE, $('._24i7u').val())
        GM_setValue(CSDN_CONTENT, $('#arthur-editor').val())
        GM_setValue(SF_TITLE, $('._24i7u').val())
        GM_setValue(SF_CONTENT, $('#arthur-editor').val())
    }

    function copyToCsdn() {
        var title = GM_getValue(CSDN_TITLE, '')
        var content = GM_getValue(CSDN_CONTENT, '')
        if (title != '' && content != '') {
            $('.article-bar__title').delay(2000).queue(function () {
                $('.article-bar__title').val(title)
                $('.editor__inner').text(content)
                GM_deleteValue(CSDN_TITLE)
                GM_deleteValue(CSDN_CONTENT)
                $(this).dequeue()
            })
        }
    }

    function copyToSegmentFault() {
        $(document).ready(function () {
            var title = GM_getValue(SF_TITLE, '')
            var content = GM_getValue(SF_CONTENT, '')
            if (title != '' && content != '') {
                $('#title').delay(2000).queue(function () {
                    $('#title').val(title)
                    GM_setClipboard(content, 'text')
                    GM_deleteValue(SF_TITLE)
                    GM_deleteValue(SF_CONTENT)
                    $(this).dequeue()
                })

            }
        })

    }

    function addCopyButton() {
        $('body').append('<div id="copyToCS">雙擊複製到CSDN和思否</div>')
        $('#copyToCS').css('width', '200px')
        $('#copyToCS').css('position', 'absolute')
        $('#copyToCS').css('top', '70px')
        $('#copyToCS').css('left', '350px')
        $('#copyToCS').css('background-color', '#28a745')
        $('#copyToCS').css('color', 'white')
        $('#copyToCS').css('font-size', 'large')
        $('#copyToCS').css('z-index', 100)
        $('#copyToCS').css('border-radius', '25px')
        $('#copyToCS').css('text-align', 'center')
        $('#copyToCS').dblclick(function () {
            saveArticle()
            GM_openInTab(SF_URL, true)
            GM_openInTab(CSDN_URL, true)
        })
        $('#copyToCS').draggable()
    }

    $(document).ready(function () {
        if (window.location.href.startsWith('https://www.jianshu.com')) {
            addCopyButton()
        } else if (window.location.href.startsWith(SF_URL)) {
            copyToSegmentFault()
        } else if (window.location.href.startsWith(CSDN_URL)) {
            copyToCsdn()
        }
    })
})()

其他注意事項

腳本編寫流程

踩了幾天坑,最後總結一下編寫油猴腳本的一點步驟。首先要思考腳本的實現方式,需要用到什麼API和權限,然後填寫好腳本的註釋信息。

然後將功能封裝成函數的形式,最後在腳本末尾調用實現的函數。寫的差不多的時候複製到瀏覽器中嘗試運行。

遇到困難的時候,可能需要直接在F12開發者工具里進行調試。有些網頁不用jQuery,為了方便,我們需要自己將jQuery導入到頁面中,可以將下面的代碼複製到瀏覽器控制台中。

var jq = document.createElement('script');
jq.src = "https://cdn.staticfile.org/jquery/3.4.1/jquery.min.js";
document.getElementsByTagName('head')[0].appendChild(jq);

發布腳本

更新URL

腳本做完了,自然是要共享出來讓大家一起使用的。當然既然要發布,自然要支持更新方便日後維護。方法也很簡單,直接在上面的註釋部分添加updateURL即可,然後設置腳本訪問地址。例如我要將腳本發布到Github上,就添加下面的註釋。

// @updateURL https://raw.githubusercontent.com/techstay/myscripts/master/tampermonkey/remind_me_vagrant_update.js

上傳腳本

油猴腳本支持好幾個網站,其中目前最主流的是GreasyFork,登錄這個網站註冊一個賬號,然後進入用戶頁面選擇提交腳本,然後填寫腳本代碼和各項信息。

這樣腳本就提交上去了,其他人也可以搜索到並安裝腳本了!

站長推薦

1.雲服務推薦: 國內主流雲服務商,各類雲產品的最新活動,優惠券領取。地址:阿里雲騰訊雲華為雲

2.廣告聯盟: 整理了目前主流的廣告聯盟平台,如果你有流量,可以作為參考選擇適合你的平台點擊進入

鏈接: http://www.fly63.com/article/detial/7398

沐鳴娛樂怎麼樣?_javascript如何判斷對象是不是為空?

JavaScript 中的所有事物都是對象:字符串、數值、數組、函數等。JavaScript 提供多個內建對象,比如 String、Date、Array 等等。 對象只是帶有屬性和方法的特殊數據類型。

JavaScript判斷對象是不是為空:

1、將對象轉為字符串,然後判斷是否等於'{}’;

var obj = {};
var objstr = jsON.stringify(obj);
if(objStr === '{}') 
return true;
else return false;

2、使用for in遍歷對象進行判斷

var obj = {};
function isEmptyObj(data) {
  for(var item in data) {
        return false;
  }return true;
};
isEmptyObj(obj); //true

3、jquery的isEmptyObject方法進行判斷

var obj = {};
$.isEmptyObject(obj); //true

4、使用Object.getOwmPropertyNames()方法

var obj = {};
function isEmptyObj(data) {
  var arr = Object.getOwmPropertyNames(data);  
  return arr.length === 0;
}
isEmptyObj(obj); //true

5、使用es6的方法Object.keys(),與4中方法相類似

function isEmptyObj(data) {
  var arr = Object.keys(data);  
  return arr.length === 0;
}
isEmptyObj(obj); //true

站長推薦

1.雲服務推薦: 國內主流雲服務商,各類雲產品的最新活動,優惠券領取。地址:阿里雲騰訊雲華為雲

2.廣告聯盟: 整理了目前主流的廣告聯盟平台,如果你有流量,可以作為參考選擇適合你的平台點擊進入

鏈接: http://www.fly63.com/article/detial/8000

沐鳴總代理_理解Javascript的柯里化

函數式編程是一種如今比較流行的編程範式,它主張將函數作為參數進行傳遞,然後返回一個沒有副作用的函數,說白了,就是希望一個函數只做一件事情。

像JavaScript,Haskell,Clojure等編程語言都支持函數式編程。

這種編程思想涵蓋了三個重要的概念:

  • 純函數
  • 柯里化
  • 高階函數

而這篇文章主要是想向大家講清楚柯里化這個概念。

什麼是柯里化

首先我們先來看一個例子:

function sum(a, b, c) {
  return a + b + c;
}
// 調用
sum(1, 2, 3); // 6

上述函數實現的是將a,b,c三個參數相加,改寫為柯里化函數如下:

function sum(a) {
  return function (b) {
    return function(c) {
        return a + b + c;
        } 
    }
}
// 調用
let sum1 = sum(1);
let sum2 = sum1(2);
sum2(3); // 6

所謂柯里化就是把具有較多參數的函數轉換成具有較少參數的函數的過程。

我們來一步步看上面那個柯里化函數做了什麼,首先第一步調用了sum(1),此時變量sum1相當於:

sum1 = function(b) {
     return function(c) {
    // 注意此時變量a存在於閉包中,可以調用,a = 1
    return a + b + c;
  }
}

然後調用sum1(2),此時賦值給變量sum2相當於:

sum2 = function(c) {
  // 變量a,b皆在閉包中, a = 1, b = 2
  return a + b + c;
}

最後調用sum2(3),返回1 + 2 + 3的結果6;

這就是一個最簡單的柯里化函數,是不是很簡單呢?

柯里化函數的作用

那麼問題來了,上面改寫后的柯里化函數和原函數比起來代碼多了不少,而且也不如原函數好理解,柯里化函數到底有什麼用呢?

確實,柯里化函數在這裏看起來的確是很臃腫,不實用,但在很多場景下他的作用是很大的,甚至很多人在不經意間已經在使用柯里化函數了。舉一個簡單的例子:

假設我們有一批的長方體,我們需要計算這些長方體的體積,實現一個如下函數:

function volume(length, width, height) {
    return length * width * height;
}
volume(200, 100, 200);
volume(200, 150, 100);
volume(200, 50, 80);
volume(100, 50, 60);

如上計算長方體的體積函數會發現存在很多相同長度的長方體,我們再用柯里化函數實現一下:

function volume(length, width, height) {
    return function(width) {
        return function(height) {
         return length * width * height;
            }
    }
}
let len200 = volume(200);
len200(100)(200);
len200(150)(100);
len200(50)(80);
volume(100)(50)(60);

如上,通過實現一個len200函數我們統一處理長度為200的長方體的體積,這就實現了參數復用

我們再舉一個只執行一次函數的例子:

function execOnce(fun) {
     let flag = true;
  return function() {
    if (flag) {
      fun && fun();
      flag = false;
    }
  }
}
let onceConsole = execOnce(function() {
    console.log('只打印一次');
});
onceConsole();
onceConsole();

如上,我們實現了一個execOnce函數,該函數接受一個函數參數,然後返回一個函數,變量flag存在閉包中,用來判斷返回的函數是否執行過,onceConsole相當於:

let onceConsole = function() {
  if (flag) {
         (function() {
        console.log('只打印一次');
      })()
      flag = false;
    }
}

這也是柯里化函數的一個簡單應用。

通用柯里化函數的實現

既然柯里化函數這麼實用,那麼我們能不能實現一個通用的柯里化函數呢?所謂通用,就是說該函數可以把函數參數轉換為柯里化函數,看下第一版實現的代碼:

 // 第一版
var curry = function (fn) {
  var args = [].slice.call(arguments, 1);
  return function() {
    var newArgs = args.concat([].slice.call(arguments));
    return fn.apply(null, newArgs);
  };
};
 function add(a, b) {
   return a + b;
 }

var addFun = curry(add, 1, 2);
addFun() // 3
//或者
var addOne = curry(add, 1);

如上代碼,我們接受一個函數作為參數,然後收集其它的參數,將這些參數傳給這個函數參數去執行。但上面的代碼有個問題,參數不夠自由,比如我們想這麼調用就會報錯:

var addFun = curry(function(a, b,c) {
  return a + b + c;
}, 1);
addFun(2)(3); // 報錯 addFun(...) is not a function

這好像違背了我們參數復用的原則,改進如下:

function curry(fn, args) {
  var length = fn.length;
  args = args || [];
  return function(...rest) {
    var _args = [...args, ...rest];
    return _args.length < length
      ? curry.call(this, fn, _args)
    : fn.apply(this, _args);
  }
}
var fn = curry(function(a, b, c) {
  console.log(a + b + c);
});
fn('a', 'b', 'c'); // abc
fn('a', 'b')('c'); // abc
fn('a')('b')('c'); // abc

如上實現就很完善,該工具函數的實現總結起來就一句話:

利用閉包將函數的參數儲存起來,等參數達到一定數量時執行函數。


後記

柯里化是以閉包為基礎的,不理解閉包可能對柯里化的理解有所阻礙,希望通過這篇文章能讓各位了解和理解JavaScript的柯里化。

能力有限,水平一般,歡迎勘誤,不勝感激。

訂閱更多文章可掃碼關注我的公眾號:

  • 回復「666」,可領取一攬子前端技術書籍;

原文地址:理解Javascript的柯里化  

站長推薦

1.雲服務推薦: 國內主流雲服務商,各類雲產品的最新活動,優惠券領取。地址:阿里雲騰訊雲華為雲

2.廣告聯盟: 整理了目前主流的廣告聯盟平台,如果你有流量,可以作為參考選擇適合你的平台點擊進入

鏈接: http://www.fly63.com/article/detial/7394