Wikipedia:In programming languages, closures (also lexical closures or function closures) are a technique for implementing lexically scoped name binding in languages with first-class functions.
译文:”在编程语言中,闭包(也词法闭包或函数闭包)是结合拥有 First-class function 的语言,实现词法作用域名的一种技术。”
First-class 指的是可以作为参数传递,可以使用return里返回,可以赋给变量的类型 Second-class 该等级类型的值可以作为参数传递,但是不能从子程序里返回,也不能赋给变量 Third-class 该等级类型的值连作为参数传递也不行
百度百科: 闭包 指可以包含自由(未绑定到特定对象)变量的代码块;这些变量不是在这个代码块内或者任何全局上下文中定义的,而是在定义代码块的环境中定义(局部变量)。
从概念上来看,维基百科的解释更加偏向于理论层面的抽象概念,而百度百科的定义则偏重实际编码中的实体。
JavaScript中的闭包
以 JavaScript 语言为例,谈一谈闭包。
首先,在 JavaScript 中几乎所有类型都可为 first-class 类型 (包括function), 所以,JavaScript 中闭包是确定可构造出来的。
由于闭包 (closure)本身与作用域(scope)息息相关,所以有必要先谈谈 JS 的作用域。
与众多语言不同的是: JavaScript 默认并无块级作用域,也就是说在花括号{}不能形成一个独立的作用域(例如 Java、C++ 中的作用域)。JavaScript是函数级作用域, 也就是每次创建一个 function 才会形成一个新的 “块级“ 作用域。
例如:
var scope ="global";
if(true){
var scope ="local";
console.log(scope) //输出local
}
console.log(scope); //输出local
假设 JavaScript 有块级作用域,明显if语句中将创建一个局部的变量scope, 在这个块中会覆盖全局定义的scope值, 所以会首先输出 “local”。但这时候块中的局部变量并不会修改在这个块外定义的变量 scope, 第二个console应该输出 “global”。
可是实际上没有这样, 两个console都会输出 “local” ,效果和去掉了{}相同。
所以 JS 没有块级作用域。
所谓函数作用域就是说:创建一个新的函数时,在函数体内部会生成新的局部作用域,其中的变量在声明它们的函数体以及这个函数体嵌套的任意函数体内都是有定义的。
比如:
var scope="global";
function t(){
var scope="local" ;
console.log(scope); //local
}
t();
console.log(scope); //global
全局定义变量scope, 函数内部又定义一次scope, 那么在函数内部的作用域中,旧的定义会被覆盖。 外部的仍然输出 “global”。
来一个稍微复杂的函数作用域的例子吧:
var g = 0; //全局作用域
function f1() {
// 这里面就形成了一个函数作用域, 能够保护其中的变量不能被外部访问
var a = 1;
console.log(g); // 函数作用域内能够访问全局作用域的变量
// 嵌套函数作用域
function f2() {
// 这里面再度形成了一个函数作用域,其中可以访问外部的f1函数作用域
var b = 2;
console.log(a);
}
console.log(b); // 出了 f2 的作用域就不能访问其中的东西了,报错 undefined
}
f1();
console.log(a); // 报错 ReferenceError: a is not defined
回顾一下前文中的概念:闭包 是指可以包含自由(未绑定到特定对象)变量的代码块;这些变量不是在这个代码块内或者任何全局上下文中定义的,而是在定义代码块的环境中定义(局部变量)。
上面的例子中,函数f2就是一个闭包,原因是:
f2中包含自由变量a; a不是在f2的代码块内定义;
a不是在任何全局上下文中定义;
a是在函数f1的内部定义(局部变量),函数f1的内部即就是定义f2这个代码块的环境
由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成“能够读取其他函数内部变量的子函数”。
当函数f1的内部函数f2被函数f1外的一个变量引用的时候,就创建了一个闭包。
以最经典的for循环为例. 大家可以试试下面这段代码,取自JavaScript 秘密花园循环中的闭包
for(var i = 0; i < 10; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
首先说说为什么最终输出的是 10 次 10, 而不是你想象中的 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
因为setTimeout是异步的!
你可以想象由于setTimeout是异步的,因此我们将这个for循环拆成 2 个部分,第一个部分专门处理 i 值的变化,第二个部分专门来做setTimeout。因此我们可以得到如下代码:
// 第一个部分
i++;
...
i++; // 总共做10次
// 第二个部分
setTimeout(function() {
console.log(i);
}, 1000);
... // 总共做10次
由于循环中的变量 i一直在变, 最终会变成 10, 而循环每每执行setTimeout时, 其中的方法还只是装入延时执行的队列,没有真正运行, 等真正到时间执行时, i 的值已经变成 10 了。i 变化的整个过程是瞬间完成的, 总之同步比异步要快, 就算setTimout是 0 毫秒也一样, 会先于你执行完成。
如果我们定义一个外部函数, 让 i 作为参数传入即可 “闭包” 我们要的变量了。
for (var i = 0; i < 10; i++) {
(function(a) {
// 变量 i 的值在传递到这个作用域时被复制给了 a,
// 因此这个值就不会随外部变量而变化了
setTimeout(function() {
console.log(a);
}, 1000);
})(i); // 我们在这里传入参数来"闭包"变量
}
那么为什么setTimeout中匿名function没有形成闭包呢?
因为setTimeout中的匿名function没有将 i 作为参数传入来固定这个变量的值,让其保留下来,而是直接引用了外部作用域中的 i,因此 i 变化时,也影响到了匿名function。
一个经典的闭包面试题:
function fun(n,o) {
console.log(o)
return {
fun:function(m){
return fun(m,n);
}
};
}
var a = fun(0); a.fun(1); a.fun(2); a.fun(3);//undefined,?,?,?
var b = fun(0).fun(1).fun(2).fun(3);//undefined,?,?,?
var c = fun(0).fun(1); c.fun(2); c.fun(3);//undefined,?,?,?
闭包的风险:
由于闭包会使得函数中的变量会被更长时间保存在内存中,消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中更是可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
在 TypeScript 中, 经常要使用 export 和 import 两个关键字, 这两个关键字和 es6 中的语法是一致的, 因为 TypeScript = es6 + type !
注意:目前没有任何浏览器实现 export 和 import ,要在浏览器中执行, 必须借助 TypeScript 或者其它的转换器!
export
export 语句用于从文件(或模块)中导出函数, 对象或者基础类型, 语法如下:
export { name1, name2, …, nameN };
export { variable1 as name1, variable2 as name2, …, nameN };
export let name1, name2, …, nameN; // also var
export let name1 = …, name2 = …, …, nameN; // also var, const
export default expression;
export default function (…) { … } // also class, function*
export default function name1(…) { … } // also class, function*
export { name1 as default, … };
export * from …;
export { name1, name2, …, nameN } from …;
export { import1 as name1, import2 as name2, …, nameN } from …;
nameN表示要导出的标识符, 可以在另一个文件中通过 import 语句导入。
有两种类型的导出, 分别对应上面的语法:
命名的导出
export { myFunction } // 导出已经声明的函数
export const foo = Math.sqrt(2) // 导出一个常量
当需要导出多个值的时候, 命名的导出就非常有用了, 在导入时, 可以使用同样的名字来引用对应的值, 示例:
// mylib.ts
export function cube(x: number): number {
return x * x * x;
}
const foo: number = Math.PI * Math.sqrt(2);
export { foo }
在另一个文件 main.ts 中, 这样使用:
// main.ts
import { cube, foo } from './mylib';
console.log(cube(3)); // 27
console.log(foo); // 4.555806215962888
默认的导出
export default function () {} // 导出默认的函数, 不使用花括号
一个文件(模块)默认的导出只能有一个, 可以是类,函数, 对象等, 示例:
// mylib.ts
export default function (x: number): number {
return x * x * x;
}
在另一个文件 main.ts 中, 这样使用:
// main.ts
import cube from './mylib';
console.log(cute(3)); // 27
import
import 与 export 对应, 用于导入其它文件(模块)导出的函数, 对象或者其他基础类型, 语法如下:
import defaultMember from "module-name";
import * as name from "module-name";
import { member } from "module-name";
import { member as alias } from "module-name";
import { member1 , member2 } from "module-name";
import { member1 , member2 as alias2 , [...] } from "module-name";
import defaultMember, { member [ , [...] ] } from "module-name";
import defaultMember, * as name from "module-name";
import "module-name";
name 用来接收导入的值的对象的名称; member, memberN 要导入的外部模块的导出名称; defaultMember 要导入的外部模块的默认导出的名称; alias, aliasN 要导入的外部模块的导出的别名; module-name 要导入的外部模块的名称, 通常是文件名; import 常见的用法有:
导入整个模块的内容, 在当前作用域插入 myModule 变量, 包含 my-module.ts 文件中全部导出的绑定:
import * as myModule from 'my-module';
导入模块的某一个导出成员, 在当前作用域插入 myMember 变量:
import { myMember } from 'my-module';
导入模块的多个导出成员, 在当前作用域插入 foo 和 bar 变量:
import {foo, bar} from 'my-module';
导入模块的成员, 并使用一个更好用的名字:
import {reallyReallyLongModuleMemberName as shortName} from 'my-module';
import {reallyReallyLongModuleMemberName as shortName, anotherLongModuleName as short} from 'my-module';
将整个模块座位附加功能导入, 但是不导入模块的额导出成员
import 'my-module';
导入模块的默认导出:
import myDefault from 'my-module';
导入模块的默认导出和命名导出:
import myDefault, * as myModule from 'my-module';
// myModule used as a namespace
或者
import myDefault, {foo, bar} from 'my-module';
// specific, named imports
]]>Sublime Text Syntax highlighting for single-file Vue.js components (enabled by vue-loader or vueify).
Vue Syntax Highlight
.Packages
folder.NOTE: You still need to install corresponding packages for pre-processors (e.g. Jade, SASS, CoffeeScript) to get proper syntax highlighting for them.
打开Sublime,按下Control + (Mac)或者Ctrl +
(Windows),然后粘贴上下面的代码:
import urllib.request,os; pf = 'Package Control.sublime-package'; ipp = sublime.installed_packages_path(); urllib.request.install_opener( urllib.request.build_opener( urllib.request.ProxyHandler()) ); open(os.path.join(ipp, pf), 'wb').write(urllib.request.urlopen( 'http://sublime.wbond.net/' + pf.replace(' ','%20')).read())
//
// BBPeiZhenFuWuVC.m
// HangyuHealth
//
// Created by lwj on 16/8/23.
// Copyright © 2016年 AntWork. All rights reserved.
//
#import "BBPeiZhenFuWuVC.h"
@interface BBPeiZhenFuWuVC () <UITableViewDataSource, UITableViewDelegate,UIWebViewDelegate>
{
UITableView *_mTableView;
UIBarButtonItem *_rightItem;
BOOL bFirstLoad;
}
@property (strong, nonatomic) UIWebView *webView;
@property WebViewJavascriptBridge* bridge;
@end
@implementation BBPeiZhenFuWuVC
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
self.title = @"陪诊服务";
[self initTableView];
[self initUI];
}
- (void) initUI {
UIButton *rightButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 44, 44)];
[rightButton setTitle:@"提交" forState:UIControlStateNormal];
rightButton.titleLabel.font = [UIFont systemFontOfSize:14.0];
[rightButton setTitleColor:[UIColor colorWithRed:32 / 255.0 green:134 / 255.0 blue:158 / 255.0 alpha:1.0] forState:UIControlStateNormal];
[rightButton setTitleColor:[UIColor whiteColor] forState:UIControlStateHighlighted];
[rightButton addTarget:self action:@selector(addBind) forControlEvents:UIControlEventTouchUpInside];
_rightItem = [[UIBarButtonItem alloc] initWithCustomView:rightButton];
}
- (void)addBind {
[_bridge callHandler:@"save" data:nil];
}
- (void)returnToPreviosView {
if(_webView.canGoBack){
[_webView goBack];
} else{
[super returnToPreviosView];
}
}
#pragma mark - UITableViewDelegate
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *cellIdentifier = [NSString stringWithFormat:@"%@Identify", NSStringFromClass([self class])];
UITableViewCell *returnCell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if(returnCell == nil)
{
returnCell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellIdentifier];
}
//清除UIWebView的缓存
[[NSURLCache sharedURLCache] removeAllCachedResponses];
_webView = [[UIWebView alloc] initWithFrame:self.view.bounds];
[self.view addSubview:_webView];
[WebViewJavascriptBridge enableLogging];
_bridge = [WebViewJavascriptBridge bridgeForWebView:_webView];
[_bridge setWebViewDelegate:self];
[_bridge registerHandler:@"finishLoading" handler:^(id data, WVJBResponseCallback responseCallback) {
[MBProgressHUD hideHUDForView:self.view animated:YES];
}];
// [_webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.baidu.com"]]];
UserDataCenter *udc = [UserDataCenter sharedInstance];
[_webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://yikangbao.yucanlin.cn:9081/registration/views/query-inventory.html?access_token=%@", udc.userInfo.strAccessToken]]]];
[returnCell.contentView addSubview:_webView];
returnCell.selectionStyle = UITableViewCellSelectionStyleNone;
return returnCell;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
NSInteger num = 1;
return num;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 1;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return _mTableView.frame.size.height;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:NO];
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
CGFloat height = 0.01;
return height;
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
UIView *view=[[UIView alloc] init];
view.backgroundColor = color_ffffff;
return view;
}
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
CGFloat height = 0.01;
return height;
}
- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
UIView *view=[[UIView alloc] init];
view.backgroundColor = color_ffffff;
return view;
}
- (void)initTableView {
_mTableView = [[TPKeyboardAvoidingTableView alloc] initWithFrame:(CGRect) {0, 0, screenWidth, self.mViewContentHeight}];
_mTableView.backgroundColor = color_f7f7f9;
_mTableView.delegate = self;
_mTableView.dataSource = self;
_mTableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine;
[_mTableView reloadData];
[self.view addSubview:_mTableView];
}
- (void)webViewDidStartLoad:(UIWebView *)webView {
[MBProgressHUD showHUDAddedTo:self.view animated:YES];
}
- (void)webViewDidFinishLoad:(UIWebView *)webView {
[MBProgressHUD hideHUDForView:self.view animated:YES];
[_bridge callHandler:@"reload" data:nil];
}
- (void)webView:(UIWebView *)webView didFailLoadWithError:(nullable NSError *)error {
[MBProgressHUD showHUD:self text:[error localizedDescription]];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
@end
]]>打开终端,用cd命令 定位到工程所在的目录,然后调用以下命名即可把每个源代码文件行数及总数统计出来:
find . "(" -name "*.m" -or -name "*.mm" -or -name "*.cpp" -or -name "*.h" -or -name "*.rss" ")" -print | xargs wc -l
其中,-name “*.m” 就表示扩展名为.m的文件。同时要统计java文件和xml文件的命令分别是:
find . "(" -name "*.java" ")" -print | xargs wc -l
find . "(" -name "*.xml" ")" -print | xargs wc -l
参考:
]]>UILabel *myLabel = [[UILabel alloc]initWithFrame:CGRectMake(15, 60, 250, 30)];
[self.view addSubview:myLabel];
//这是我们的测试用的文本字符串数据
NSString *content = @"abc123a1b2c3你懂得888";
NSMutableAttributedString *attributeString = [[NSMutableAttributedString alloc]initWithString:content];
[attributeString setAttributes:@{NSForegroundColorAttributeName:[UIColor blueColor],NSFontAttributeName:[UIFont systemFontOfSize:25]} range:NSMakeRange(0, 10)];
myLabel.attributedText = attributeString;
.controller('DashCtrl', function($scope, $cordovaDialogs) {
$cordovaDialogs.alert('message', 'title', 'button name')
.then(function() {
// callback success
});
$cordovaDialogs.confirm('message', 'title', ['button 1','button 2'])
.then(function(buttonIndex) {
// no button = 0, 'OK' = 1, 'Cancel' = 2
var btnIndex = buttonIndex;
});
$cordovaDialogs.prompt('msg', 'title', ['btn 1','btn 2'], 'default text')
.then(function(result) {
var input = result.input1;
// no button = 0, 'OK' = 1, 'Cancel' = 2
var btnIndex = result.buttonIndex;
});
// beep 3 times
$cordovaDialogs.beep(3);
})
.controller('DashCtrl', function($ionicPlatform, $scope, $cordovaActionSheet) {
$ionicPlatform.ready(function() {
var options = {
title: '你要做什么?',
buttonLabels: ['分享到陈斌彬的技术博客', '分享到朋友圈'],
addCancelButtonWithLabel: '取消',
androidEnableCancelButton: true,
winphoneEnableCancelButton: true,
addDestructiveButtonWithLabel: '删除这个'
};
$scope.showToast = function() {
// $cordovaToast.show(uuid, 'long', 'center');
$cordovaActionSheet.show(options)
.then(function(btnIndex) {
var index = btnIndex;
});
}
});
})
.controller('DashCtrl', function($ionicPlatform, $scope, $cordovaToast) {
$ionicPlatform.ready(function() {
$cordovaToast.showShortTop('Hello World!!');
$scope.showToast = function() {
$cordovaToast
.show('You clicked a button!!', 'long', 'center');
}
});
})
]]>npm install -g cordova
cd my_project
cordova platform update ios@4.0.0
MLAutoReplace https://github.com/molon/MLAutoReplace
Auto-Importer-for-Xcode https://github.com/citrusbyte/Auto-Importer-for-Xcode
Cichlid https://github.com/dealforest/Cichlid
CleanHeaders-Xcode https://github.com/insanoid/CleanHeaders-Xcode
]]>