分类 未分类 下的文章
在sublime text中修改变量名
光标在变量名旁边按下alt+F3, 就会多光标选中所有的变量, 此时可以修改.
js中的global与window
在浏览器中, window是用来"兜底"的对象, 所有默认定义的对象都是window的子对象.
var a = {}
window.a //{}
parseInt === window.parseInt //true
而在node.js中没有window, 对等关系的是global
var a = {}
global.a //{}
parseInt === global.parseInt //true
那么在cocos creator中, 究竟是哪一个呢? 测试了一下, 是window. global是不存在的.
那么electron呢? electron中,两者都存在, 并且两者其实是同一个.
//electron中
window === global //true
js的typeof与instanceof
typeof得出的是数据类型, 如:
typeof(1) //'number'
typeof("abc") //'string'
typeof(true) //'boolean'
var a = {}
typeof(a) //'object'
var b = []
typeof(b) // 'object'
注意最后两句, 一个array的type是object,是不是有些奇怪? 因为, array其实不是基本类型. 那怎么知道array是array呢? 那就要用object的判断方式, instanceof
var b = []
b instanceof Array //true
b instanceof Object //true
1 instanceof Number //false
"abc" instanceof String //false
注意最后两句, 1和"abc"是基本类型, 如果是要Number和String类型的,需要用 new Number和new String来获取
var n = new Number(1)
n instanceof Number //true
然而对于函数function又有点不一样了
var f = function(){}
typeof(f) //function
f instanceof Function //true
f instanceof Object //true
您可以看到, f虽然是一个Object,然而用typeof却可以得到他的类型.
再看看RegExp, 和Array是类似的
typeof(/a/) //object
/a/ instanceof RegExp //true
/a/ instanceof Object //true
用mocha做node.js的单元测试
js单元测试有好多个方式, 按照诞生时间依次是Qunit, jasmine, mocha, ava. 其中, ava是15年的项目, 还很新. Qunit和ava的star人数还没有超过1万, jasmine和mocha平分秋色,基本差不多. 考虑到jasmine是比mocha老的项目,而且jasmine最早是针对浏览器端的,而mocha似乎开始时就是针对node.js的, 这儿就选择mocha了.
安装使用
全局安装mocha
npm install -g mocha
项目本地安装chai(assert库)
npm install chai
chai库提供多种assert方式, 后面将举例用expect
示例被测项目, 叫add.js
function add(a, b){
return a + b
}
function minus(a,b){
return a - b
}
module.exports = add
测试代码, 叫test.js
var expect = require("chai").expect
var add = require("add")
describe("add", function(){
it("1+2=3", function(){
expect(add(1,2)).to.be.equal(3)
})
})
运行代码
mocha test.js
js经常需要对比object,一般用意是对比内容key和value是否一致,就不能用expect().to.be.equal()了,需要用
expect().to.deep.equal()
其他方面
测试private function,以及mock.
例如, 上面的minus()函数没有通过module.exports导出, 是个私有函数, 如何进行测试? 这儿需要用到rewire. 将rewire安装到项目中.
npm install rewire
测试minus的文件test2.js
var expect = require("chai").expect
var rewire = require('rewire') // 使用rewire加载被测文件.
var add = rewire("add")
describe("add", function(){
var minus = add.__get__("minus") // 使用__get__获取私有函数
it("1-2=-3", function(){
expect(minus(1,2)).to.be.equal(-1)
})
})
rewire还可以使用set来mock函数, 看这儿
关于更多chai的用法, 看这儿
最佳实践
- 装所有测试文件的目录命名为
test
,然后在目录上一级运行mocha
可以测试所有test中的文件. - 文件名为 xx.test.js或者xx_spec.js ,spec是inspect(核查)的简称.
- 使用
npm test
运行测试, 需要在package.json中加入:"scripts": { "test": "mocha" }
如何让浏览器下载一张图片而不是打开它?
网上流传的许多并不能用的答案, 常见的是这样的:
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<button onclick="down()">down</button>
<script type="text/javascript">
function down(){
location = "/downloadpic/icon.png"
var pic = window.open(location,"a1")
pic.document.execCommand("SaveAs")
}
</script>
</body>
</html>
这儿的问题是, window.open只会打开新窗口, 并且在新窗口中打开文件. 如果文件是图片, 浏览器只会显示而不是下载.
并且, pic.document.execCommand("SaveAs") 这一句似乎并没有什么用.
下面是正确答案. 使用标签, 加download属性, 就这么简单.
<body>
<a href="icon.png" download>download icon</a>
</body>
还可以为download属性指定下载文件名称:
<body>
<a href="icon.png" download="new_icon.png">download icon</a>
</body>
js 时间回调函数
在chrome和node中有效
定时连续回调
iv = setInterval(callback, milsec)
clearInterval(iv)
javascript的lint工具那么多,还是eslint比较好用啊.
有jslint, javascriptlint(jsl), 还有eslint. 貌似eslint是最受欢迎的.
安装eslint
npm i -g eslint
运行:
eslint *.js
在js目录下放一个.eslintrc.json文件, 可以对需要lint的规则做设置.下面是我复制cocos creator的配置,并做了一点修改, 忽略了末尾分号检测.
{
"extends": "eslint:recommended",
"rules": {
"comma-dangle": 0,
"no-console": 0,
"no-constant-condition": 0,
"semi": 0,
"no-unused-vars": 1
},
"env": {
"browser": true,
"node": true,
"es6": true,
"mocha": true
},
"plugins": [
],
"globals": {
"cc": false,
"CC_EDITOR": false,
"CC_DEV": false,
"CC_JSB": false,
"_ccsg": false
}
}
websocket使用chrome客户端REPL和node.js服务端REPL
在REPL中比较容易调试和发现问题, 所以这儿介绍REPL的玩法.
node.js服务端需要安装模块.
npm install ws
进入node, 开启服务,等待客户端连接. 为了便于以后向客户端发送消息, 把连接后的ws对象赋值给全局变量thisws
node
>
var thisws
var Wss = require('./ws').Server
var wss = new Wss({port: 3888})
wss.on('connection', function(ws){
thisws = ws
console.log('connected.')
ws.on('message', function(message){
console.log("msg: ",message)
})
})
chrome 打开console,进行连接和监听服务器消息
s = new WebSocket('ws://localhost:3888')
s.onmessage = function(e){console.log(e.data)}
好啦!, 在chrome中send消息可以在node那边收到, 在node中send消息也可以在chrome中收到了
先从chrome中send
s.send('我是chrome') // 在node中将看到这条消息.
再从node中send
thisws.send('我是node服务器') //在chrome中将看到这条消息.
当然,除了chrome自带的WebSocket客户端, 在node中ws也提供客户端, 另外,socket.io也是另一种websocket客户端和服务器的提供者.
- 总结
浏览器客户端监听和发送
onclose = function(e){}
onmessage = function(e){}
onopen = function(){}
onerror = function(){}
send(string)
close()
node ws服务端监听和发送
wss.on('connection',function(ws){})
wss.on('error', function(err){})
wss.on('header',function(array){})
wss.on('listening',function(){})
ws.on('message',function(message_string,flags){})
ws.on('close',function(code, reason){})
ws.on('error',function(err){})
ws.on('open',function(){})
ws.onclose = function(e){}
ws.onerror = function(e){}
ws.onmessage = function(e){}
ws.onopen = function(e){}
ws.readyState
ws.send(message_string, function(error){})
ws.close(code, reason)
// 所有客户端列表
wss.clients
wss.close(function(){})
将Electron的APP打包为UWP的appx包
开始以前,准备好visual studio 2015, 现在社区版是免费的.这玩意儿很大, 需要装个半天的.
先安装由微软推出的Electron打包为uwp工具 (真的是微软推出啊! 微软现在竟然这么拥抱开源...)
npm install -g electron-windows-store
然后进入管理员权限的powershell运行:
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned
OK, 接下来就打包了, 举我自己的app的例子:
electron-windows-store --input-directory C:\r\vueproj\nhproj\nhpe --output-directory C:\r\vueproj\nhproj\nhpe_uwp --flatten true --package-version 1.0.0.0 --package-name nhpe
哦, 真的好长, 幸好里面的参数还可以简写:
electron-windows-store -i C:\r\vueproj\nhproj\nhpe -o C:\r\vueproj\nhproj\nhpe_uwp -f true -p 1.0.0.0 -n nhpe
需要注意的是, --package-name一项, 名称必须是app运行文件的名称. 比如, 我已经把electron.exe修改为nhpe.exe了,这儿就一定要填写nphe
然后回车后要提示:
? Did you download and install the Desktop App Converter? It is *not* required to run this tool. No
这一步不是必须的, 回答no即可. 如果yes的话, 你需要去这儿安装Desktop App Converter,不过还是懒得麻烦了.
回车后提示:
? You need to install a development certificate in order to run your app. Would you like us to create one? Yes
这一步必须yes, 否则虽然可以打包成功,然而打好的包由于没有签名而不能运行. 继续回车:
? Please enter your publisher identity: CN=idarc
? Please enter the location of your Windows Kit's bin folder: C:\Program Files (x86)\Windows Kits\10\bin\x64
publisher identity这一步必须填写为 CN=xxxx 的格式,否则后面会报错的...
Windows Kit这一步, 如果visual studio 装的OK的话, 直接回车就可以了.
接下来会弹窗让你输入密码. 不用输入密码, 点击"none"即可.
等待一会儿, 如果没有错误的话, 就会在指定的目录下看到nhpe.appx, 双击运行安装即可.
安装完了再运行, 哦, 这货和直接运行electron.exe改名的nhpe.exe为毛没有任何外观上的区别? 微软说区别是可以获取在在其他xbox等上运行的能力,然而我并没有xbox...
js如何判断一个对象没有任何自己的属性(为空)?
参考原文
假如有两个空对象, 判断他们是否相等, 结果一定是false:
var a = {}
var b = {}
a == b // false
因为跟c语言类似, 这儿判断的是地址而不是内容.
那么怎么知道a是空呢?
有三个方法. 这儿先假设有一个对象obj
var obj = {}
方法1
Object.keys(obj).length === 0
方法2
Object.getOwnPropertyNames(obj).length === 0
方法3, 自己判断
// Speed up calls to hasOwnProperty
var hasOwnProperty = Object.prototype.hasOwnProperty;
function isEmpty(obj) {
// null and undefined are "empty"
if (obj == null) return true;
// Assume if it has a length property with a non-zero value
// that that property is correct.
if (obj.length > 0) return false;
if (obj.length === 0) return true;
// If it isn't an object at this point
// it is empty, but it can't be anything *but* empty
// Is it empty? Depends on your application.
if (typeof obj !== "object") return true;
// Otherwise, does it have any properties of its own?
// Note that this doesn't handle
// toString and valueOf enumeration bugs in IE < 9
for (var key in obj) {
if (hasOwnProperty.call(obj, key)) return false;
}
return true;
}
// test it
isEmpty(""), // true
isEmpty(33), // true (arguably could be a TypeError)
isEmpty([]), // true
isEmpty({}), // true
isEmpty({length: 0, custom_property: []}), // true
isEmpty("Hello"), // false
isEmpty([1,2,3]), // false
isEmpty({test: 1}), // false
isEmpty({length: 3, custom_property: [1,2,3]}) // false
在chrome console和node.js的REPL中使用严格模式
在chrome中你只能这么做了:
(function()
{
'use strict';
var foo = 123;//works fine
bar = 345;//ReferenceError: bar is not defined
}());
上面这种模式被称之为IIFE(Immediately Invoked Function Expression)即刻执行的函数表达式.
在node中, 你当然也可以用上这种方式, 不过还有一种方式, 在启动node的时候使用--use-strict参数
node --use_strict
如果是真的,国内原创音乐令人惊讶
试听win10上的网易云音乐的轻音乐, 发现即便是自动推荐的, 也有很多蛮好听的曲子. 去看了一下作曲者, 竟然很多是国内的原创音乐人. 比如这个: 饭碗的彼岸, 就有好几首好听的曲子《小河》《小樱》。
感觉真是高手在民间啊。
java的交互式命令环境(REPL)
java8及其以前是不支持交互式命令环境REPL的.
Java9 将引入一个JShell来支持. 但目前Java9还没有推出, 怎么办呢? 有一个github的项目, 支持java的REPL
使用前, 需要安装JDK(不是JRE), 以及gradle. 下面是命令行中的安装方法
git clone https://github.com/albertlatacz/java-repl.git
cd java-repl
gradle shadowJar
cd build/libs
java -jar javarepl-dev.jar
需要注意的是,gradle这一步需要执行数分钟, 我的电脑上是8分多钟.
java运行这一步, 需要是类似于jdk1.8.0_111/bin/java这样的目录
cocos creator 音频部分控制
由于当前cocos creator音频部分的文档还没有, 这儿简单介绍一下音频部分.
- 普通的音频的组件是cc.AudioSource, 可以附加在一个node节点上, 同时只能播放一个音频, 添加组件后, 需要将音频拖放到Clip一栏里面(属性为audio-clip).
组件可以进行的操作包括播放/暂停/停止/继续
audioSource.play()
audioSource.pause()
audioSource.stop()
audioSource.resume()
可以获取播放进度和总长度
audioSource.getCurrentTime() //单位为秒的浮点数
audioSource.getDuration() //同样是单位为秒的浮点数
从属性浏览器中可以发现还有如下属性:
clip
volume
mute
loop
playOnLoad
preload
- 使用音频引擎
引擎是一个cc下的对象cc.audioEngine来操作的.
引擎并不需要去操作一个node对象, 而是直接操作一个资源管理器里面的源文件.所以在定义cc.Class的时候与audioSource不同. 以url的方式定义, 音乐文件(很可能)需要放在resources目录中.
// 在操作audioSource的AudioSourceControl.js文件中, 定义属性为cc.AudioSource类型. 然后, 从资源管理器中拖动一个音乐文件到audiosource节点的clip上. 再从层级管理器中拖动audiosource节点到另一个节点的AudioSourceControl.js属性的audioSource上
cc.Class({
properties: {
audioSource: {
type: cc.AudioSource,
default: null
},
// 省略其他代码
})
// 在操作audioEngine的AudioEngineControl.js文件中, 定义**url**为cc.AudioClip类型. 然后, 从资源管理器中拖动一个音乐文件到使用此AudioEngineControl.js的节点的audio属性上.
// 从示例代码中来看, 这个文件是放在resources目录中的,也就是说, 需要动态加载的.
cc.Class({
properties: {
audio: {
url: cc.AudioClip,
default: null
},
// 省略其他代码
})
使用引擎播放, 每次会产生一个audio id, 记住这个id, 用来在后面对该播放中的音乐进行操作.
// 播放,暂停,继续, 停止
var id = cc.audioEngine.play(this.audio, false, 1); //参数分别为filepath, loop, volume. filepath示例为res/raw-assets/resources/audio/music_logo.mp3, 如果是拖进来的就不用管这个了.
cc.audioEngine.pause(id)
cc.audioEngine.resume(id)
cc.audioEngine.stop(id)
对所有的播放中音乐统一操作:
cc.audioEngine.stopAll();
cc.audioEngine.pauseAll();
cc.audioEngine.resumeAll();
获取各种状态
cc.audioEngine.getCurrentTime(id) // 秒数, 有对应set方法
cc.audioEngine.getDuration(id) // 秒数
cc.audioEngine.getStatus(id) // 返回值-1: 停止状态, 1: 播放状态, 2: 暂停状态.
cc.audioEngine.getVolume(id) // 有对应set方法
- 下面放出cocos creator的示例代码, 先是AudioSourceControl
cc.Class({
extends: cc.Component,
properties: {
audioSource: {
type: cc.AudioSource,
default: null
},
label: {
type: cc.Label,
default: null
}
},
// use this for initialization
onLoad: function () {
// cc.audioEngine.setMaxWebAudioSize(1024*10);
},
update: function () {
if (!this.label) {
return;
}
var audio = this.audioSource;
this.label.string = audio.getCurrentTime().toFixed(1) + ' s / ' + audio.getDuration().toFixed(1) + ' s';
},
play: function () {
this.audioSource.play();
},
pause: function () {
this.audioSource.pause();
},
stop: function () {
this.audioSource.stop();
},
resume: function () {
this.audioSource.resume();
}
});
- 再是audioEngineControl
cc.Class({
extends: cc.Component,
properties: {
audio: {
url: cc.AudioClip,
default: null
},
label: {
type: cc.Label,
default: null
}
},
onLoad: function () {
this.maxNum = cc.audioEngine.getMaxAudioInstance();
this.audioPool = [];
// check deprecated
['playMusic', 'playEffect'].forEach(function (name) {
if (!cc.audioEngine[name]) {
cc.warn('.' + name + ' is not found!');
}
});
},
update: function () {
if (!this.label) return;
for (var i=0; i<this.audioPool.length; i++) {
var id = this.audioPool[i];
var state = cc.audioEngine.getState(id);
if (state < 0) {
this.audioPool.splice(i, 1);
i--;
}
}
this.label.string = 'Instance: ' + this.audioPool.length + ' / ' + this.maxNum;
},
play: function () {
if (!this.audio) return;
var id = cc.audioEngine.play(this.audio, false, 1);
this.audioPool.push(id);
},
stopAll: function () {
if (!this.audio) return;
cc.audioEngine.stopAll();
},
pauseAll: function () {
if (!this.audio) return;
cc.audioEngine.pauseAll();
},
resumeAll: function () {
if (!this.audio) return;
cc.audioEngine.resumeAll();
},
});