Ref: React从入门到精通视频教程
Ref: C# 教程
Ref: [Unity3D] C# Basic : Gameplay Scripting
/* 之前的js总结有点low, 这次通过对比c#相关特性掌握js */
打印
js
console.log("...")
alart("...") c#
Console.WriteLine("...");
print("...") // for unity.
注释 ...
变量 - 类型判断
js
typeof(<var>) c#
<object>.GetType()
变量 - 类型转换
js
> var a = 120
undefined
> var b = "kg"
undefined
> a + b
'120kg' c#
String str = "hao";
int a = 123;
Console.WriteLine(str + a); // 自动 .toString()
> hao123
变量 - String
js
c#
Goto: String 类的方法
数组
js
var a = Array(4)
<arr>.push(...)
<arr>.pop()
<arr>.shift() // 删除第一个元素 delete <arr>[3] // 不是删除而是清空变为undefined.
<arr>.splice(3) // 这个才是删除 <arr>.concat(<arr2>) c#
double[] balance = new double[10];
int[] marks = new int[5] { 99, 98, 92, 97, 95}; // 这里的5必须与后面的个数一致
int[] score = marks; // 指向相同的内存位置
条件判断 - 等价
js
Ref: Javascript 中 == 和 === 区别是什么?
一些违反直觉的结果:【'0'不是遵循ascii,是从0开始计算】
值判断:数值、字符串、布尔值
地址判断:对象、数组、函数
> 0 == ''
true
> 0 == '0'
true
> false == '0'
true
> null == undefined
true
> '\t\r\n' == 0
true
> undefined == true
false
> undefined == false
false
> !undefined
true
> !null
true
> a = 'hao'
'hao'
> b = 'hao'
'hao'
> a == b
true
> a === b
true
c#
Ref: C#中的==、Equal、ReferenceEqual
ReferenceEquals: 是否是同一个对象的不同引用【指向同一个对象,当然相等,比较严格】
自定义类型需要重载==,否则没法使用(编译)
字符串比较:==效果等价equal
using System.IO;
using System; class Program
{
static void Main()
{
Console.WriteLine("Hello, World!"); String str1 = "hao";
String str2 = "hao"; Console.WriteLine( str1.GetHashCode() ); // output: 696029526
Console.WriteLine( str2.GetHashCode() ); // output: 同上 if (Object.ReferenceEquals(str1, str2))
{
Console.WriteLine("ReferenceEquals returns true"); // 会执行
}
}
}
条件判断 - switch ...
循环 - while ...
循环 - for
js
- for (... in ... ) 方法 - 遍历对象的key
- for (... of ... ) 方法 - 遍历对象的value
Ref: javascript总for of和for in的区别?
> let aArray = ['a',123,{a:'1',b:'2'}]
undefined
> aArray
[ 'a', 123, { a: '1', b: '2' } ] -----------------------------------------
> for(let index in aArray){
... console.log(`${aArray[index]}`);
... }
a
123
[object Object]
undefined -----------------------------------------
> for(var value of aArray){
... console.log(value);
... }
a
123
{ a: '1', b: '2' } // <---- 这才是想要的
进一步的,给数组添加一个属性,
> aArray.name = 'demo'
'demo'
> aArray
[ 'a', 123, { a: '1', b: '2' }, name: 'demo' ]
Jeff: 属性就是属性,不能算是数组的表现部分。
但是,for ... in会打印出新添加的属性;for ... of 则不会。
如果实在想用for...of
来遍历普通对象的属性的话,可以通过和Object.keys()
搭配使用,
先获取对象的所有key的数组,然后遍历:
var student={
name:'wujunchuan',
age:22,
locate:{
country:'china',
city: 'xiamen',
school: 'XMUT'
}
}
for(var key of Object.keys(student)){
//使用Object.keys()方法获取对象key的数组
console.log(key+": "+student[key]);
}
c#
Ref: C#中数组Array、ArrayList、泛型List<T>的比较
- ArrayList类型
ArrayList myAL = new ArrayList();
myAL.Add("Hello");
myAL.Add("World");
myAL.Add("!");
foreach ( Object obj in myAL )
Console.Write( "{0}", obj );
小问题:
ArrayList解决了数组中所有的缺点,但是在存储或检索值类型时通常发生装箱和取消装箱操作,带来很大的性能耗损。尤其是装箱操作
这样在ArrayList中插入不同类型的数据是允许的。因为ArrayList会把所有插入其中的数据当作为object类型来处理;
在使用ArrayList处理数据时,很可能会报类型不匹配的错误,也就是ArrayList不是类型安全的。
解决方法就是泛型。
- List<T>类型
List<T> 是类型安全的,在声明List集合时,必须为其声明List集合内数据的对象类型。
List<string> dinosaurs = new List<string>();
dinosaurs.Add("Tyrannosaurus");
dinosaurs.Add("Amargasaurus"); foreach (string dinosaur in dinosaurs)
{
Console.WriteLine(dinosaur);
}
难点,喝杯茶,研究下
函数 - 匿名函数
js
1. 基本形式为:【使用圆括号】
( function(){ <这里是块级作用域> } )(); // 有了后面的(),匿名函数 --> 立即执行函数
( function () { /* code */ } () );
注意:匿名函数的作用是避免全局变量的污染以及函数名的冲突。
2. 如果不想用圆括号:
因为Javascript将function关键字当作一个函数声明的开始,而函数声明后面不能加圆括号,如果你不显示告诉编译器,它会默认生成一个缺少名字的function,并且抛出一个语法错误。
Sol: 在function前面加个小东西就好了,如更多的写法,详见:JS匿名函数理解
使用-/+运算符
-function(x,y){
alert(x+y);
return x+y;
}(3,4); 或者+, --, ++
使用波浪符(~)
等等……
还有一些比较奇怪的写法,Ref: 杂七杂八JS :深入理解 函数、匿名函数、自执行函数
~function(x){
alert(x);
}(5);//5
> -1
---------------------
!function(x){
alert(x);
}(4);//4
> true
---------------------
0, function(x){
alert(x);
}(3);//3
> undefined
---------------------
true && function(x){
alert(x);
}(2);//2
> undefined
---------------------
false && function(x){
alert(x);
}(2);//2
> false
3. 为什么需要匿名函数
<script type="text/javascript">
var s = document.getElementsByTagName('input'); //获取整个网页的标签
window.onload = function() {
for(var i = 0;i < s.length;i++){
s[i].onclick = show(i); // js 没有块级作用域,你用循环赋值给每一个 s[i] 的方法的参数都是一个i的引用
}
};
function show(num){
alert("你好,请"+num+"号嘉宾领奖")
}
</script>
方案:利用 IIFE(立即执行函数)模拟了一个块级作用域解决了这个问题。
你声明了一个匿名函数,同时立即执行它自己。num 是函数接受的参数,i是执行函数时传入的值。
4. 箭头函数(Lambda表达式)
匿名函数有两种语法风格:Lambda表达式(lambda-expression)和匿名方法表达式(anonymous-method-expression)。在几乎所有的情况下,Lambda表达式都比匿名方法表达式更为简介具有表现力。
箭头函数要实现类似纯函数的效果,必须剔除外部状态。所以当你定义一个箭头函数,在普通函数里常见的this
、arguments
、caller
是统统没有的。
什么是this
首先,什么是this, 参见:Javascript的this用法(阮一峰)
this就是调用函数的那个对象。
- 情况一:纯粹的函数调用
var x = 1; function test(){
this.x = 0; // this就代表全局对象Global
}
test();
alert(x); //
注意:与下面两个相比,没有对象o;有了对象后,this的范围由global变为了对象范围内。
- 情况二:作为对象方法的调用
function test(){
alert(this.x); // this就指这个上级对象
} var o = {};
o.x = 1; o.m = test;
o.m(); //
- 情况三:作为构造函数调用
function test(){
this.x = 1;
} var o = new test();
alert(o.x); //
- 情况四:apply调用
apply()是函数对象的一个方法,它的作用是改变函数的调用对象,它的第一个参数就表示改变后的调用这个函数的对象。因此,this指的就是这第一个参数。
var x = 0; function test(){
alert(this.x);
} var o={};
o.x = 1;
o.m = test;
o.m.apply(); //0, apply()的参数为空时,默认调用全局对象 或者:
o.m.apply(o); //1, 这时this代表的是对象o
匿名函数没有this
在匿名函数中,没有this, arguments, caller。
function foo() {
this.a = 1
let b = () => console.log(this.a) // 不是匿名函数的this,而是外层函数foo的this b()
} foo() // 1
同理:
function foo() {
return () => console.log(arguments[0]) // 同理,这也是外层函数foo的arguments,1,而非3。
} foo(1, 2)(3, 4) //
易犯错
- 一个经常犯的错误是使用箭头函数定义对象的方法,如:
let a = {
foo: 1,
bar: () => console.log(this.foo) // 对象a
并不能构成一个作用域,所以再往上 反而到达了全局作用域,所以不是a.foo
} a.bar() //undefined
let
允许你声明一个作用域被限制在块级中的变量、语句或者表达式。
与var关键字不同的是,它声明的变量只能是全局或者整个函数块的。
- 另一个错误是在原型上使用箭头函数,如:
function A() {
this.foo = 1
} A.prototype.bar = () => console.log(this.foo) // this不是指向A,,而是根据变量查找规则回溯到了全局作用域 let a = new A()
a.bar() //undefined
通过以上说明,我们可以看出,箭头函数除了传入的参数之外,真的是什么都没有!
如果你在箭头函数引用了this
、arguments
或者参数之外的变量,那它们一定不是箭头函数本身包含的,而是从父级作用域继承的。
在Javascript语言体系中,是不存在类(Class)的概念的,javascript中不是基于‘类的',而是通过构造函数(constructor)和原型链(prototype chains)实现的。
为了解决构造函数的对象实例之间无法共享属性的缺点,js提供了prototype属性。
5. 什么情况下该使用箭头函数
- 箭头函数适合于无复杂逻辑或者无副作用的纯函数场景下,例如用在
map
、reduce
、filter
的回调函数定义中; - 不要在最外层定义箭头函数,因为在函数内部操作
this
会很容易污染全局作用域。最起码在箭头函数外部包一层普通函数,将this
控制在可见的范围内; - 如开头所述,箭头函数最吸引人的地方是简洁。在有多层函数嵌套的情况下,箭头函数的简洁性并没有很大的提升,反而影响了函数的作用范围的识别度,这种情况不建议使用箭头函数。
比如,在对象中定义的简单的方法,匿名函数看上去会简洁一些。
毕竟,有了beyoud.showArtist,没必要再给函数在取一个名字。
c#
一些匿名函数的示例
x => x + //隐式的类型化,函数体为表达式
x => {return x + ;} //隐式的类型化,函数体为代码块
(int x) => x + //显式的类型化,函数体为表达式
(int x) => {return x + ;} //显式的类型化,函数体为代码块
(x , y) => x * y //多参数
() => Console.WriteLine() //无参数
async (t1 , t2) => await t1 + await t2 //异步
delegate (int x) {return x + ;} //匿名函数方法表达式
delegate {return + ;} //参数列表省略
Lambda表达式和匿名方法表达式的区别:
● 当没有参数的时候,匿名方法表达式允许完全省略参数列表,从而可以转换为具有任意值参数列表的委托类型,Lambda表达式则不能省略参数列表的圆括号()。
● Lambda表达式允许省略和推断类型参数,而匿名方法表达式要求显式声明参数类型。
● Lambda表达式主体可以为表达式或者代码块,而匿名方法表达式的主体必须为代码块。
● 只有Lambda表达式可以兼容到表达式树类型。
Ref: C# Lambda expressions: Why should I use them?
简单的嵌入式的函数,采用匿名方式会阅读更友好一些。
// anonymous delegate
var evens = Enumerable
.Range(, )
.Where(delegate(int x) { return (x % ) == ; })
.ToList(); // lambda expression
var evens = Enumerable
.Range(, )
.Where(x => (x % ) == ) // <---- 这里看上去格式简洁了许多!
.ToList();
课外阅读
委托,定义非常类似于函数,但不带函数体。类似于 C 或 C++ 中函数的指针。
using System; delegate int NumberChanger(int n); // 声明一个委托delegate:引用带有一个整型参数的方法,并返回一个整型值
namespace DelegateAppl
{
class TestDelegate
{
static int num = ;
public static int AddNum(int p)
{
num += p;
return num;
} public static int MultNum(int q)
{
num *= q;
return num;
}
public static int getNum()
{
return num;
} static void Main(string[] args)
{
// 创建委托实例
NumberChanger nc1 = new NumberChanger(AddNum);
NumberChanger nc2 = new NumberChanger(MultNum);
// 使用委托对象调用方法
nc1();
Console.WriteLine("Value of Num: {0}", getNum());
nc2();
Console.WriteLine("Value of Num: {0}", getNum());
Console.ReadKey();
}
}
}
通过事件使用委托,在类的内部声明事件,首先必须声明该事件的委托类型;然后,声明事件本身,使用 event 关键字。
public delegate void BoilerLogHandler(string status); // 好比一个“函数指针类型” // 基于上面的委托定义事件
public event BoilerLogHandler BoilerEventLog;
之后,该事件在生成的时候会调用委托;有点trigger函数的意思。
事件使用 发布-订阅(publisher-subscriber) 模型。
- 包含事件的类用于发布事件 ---- 发布器(publisher) 类
- 接受该事件的类 ---- 订阅器(subscriber) 类
using System;
namespace SimpleEvent
{
using System;
/***********发布器类***********/
public class EventTest
{
private int value; public delegate void NumManipulationHandler(); public event NumManipulationHandler ChangeNum;
protected virtual void OnNumChanged()
{
if ( ChangeNum != null )
{
ChangeNum(); /* 事件被触发 */
}else {
Console.WriteLine( "event not fire" );
Console.ReadKey(); /* 回车继续 */
}
} public EventTest()
{
int n = ;
SetValue( n );
} public void SetValue( int n )
{
if ( value != n )
{
value = n;
OnNumChanged();
}
}
} /***********订阅器类***********/ public class subscribEvent
{
public void printf()
{
Console.WriteLine( "event fire" );
Console.ReadKey(); /* 回车继续 */
}
}
/**************************/
/***********触发***********/
/*************************/
public class MainClass
{
public static void Main()
{
EventTest e = new EventTest(); /* 实例化对象,第一次没有触发事件 */
subscribEvent v = new subscribEvent(); /* 实例化对象 */
e.ChangeNum += new EventTest.NumManipulationHandler( v.printf ); /* 注册,看看有多少人要订阅这个事件触发 */
e.SetValue( );
e.SetValue( );
}
}
}
函数 - 闭包
js
在函数外部,自然无法读取函数内的局部变量。
函数内部声明变量的时候,一定要使用var命令。如果不用的话,你实际上声明了一个全局变量!
如何从外部读取局部变量?
function f1(){
var n=999;
function f2(){
alert(n);
}
return f2;
} var result=f1();
result(); //
让这些变量的值始终保持在内存中。
为什么会这样呢?原因就在于f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。
【可能的问题需注意:内存消耗很大】
function f1(){ var n=999;
nAdd=function() { n+=1 } function f2(){
alert(n);
} return f2;
} var result=f1(); result(); // 999, 这里我们使用了f1内部的n。
nAdd(); // 说明起了作用,让n改变了,且一直保存在内存中
result(); //
思考:如何调用object中的name,如下所示。通过var that,这样this就不是global了。
var name = "The Window"; var object = {
name : "My Object", getNameFunc : function() {
// var that = this;
return function(){
return this.name;
// return that.name;
};
}
}; alert(object.getNameFunc()());
c#
C#中通常通过匿名函数和lamada表达式来实现闭包。
Ref: C# 闭包问题-你被”坑“过吗?
public static void Main()
{
Console.WriteLine("Starting."); for (int i = ; i < ; ++i)
{
int j = i;
Task.Run( () => Console.WriteLine(j) ); // 如果采用i, 将传入的是引用
} Console.WriteLine("Finished. Press <ENTER> to exit.");
Console.ReadLine();
}
从本质上说,闭包是一段可以在晚些时候执行的代码块,但是这段代码块依然维护着它第一个被创建时环境(执行上下文)。
using System; class Test
{
static void Main()
{
Action action = CreateAction();
action();
action();
}
--------------------------------------------------
static Action CreateAction()
{
int counter = ;
return delegate // 好比函数指针!
{
// Yes, it could be done in one statement;
// but it is clearer like this.
counter++;
Console.WriteLine("counter={0}", counter);
};
}
}
C#中通常通过"匿名函数"和"lamada表达式"来实现闭包。
var values = new List<int> { , , };
var funcs = new List<Func<int>>(); foreach (var v in values)
funcs.Add( () =>
{
//Console.WriteLine(v);
return v;
} ); foreach (var f in funcs)
Console.WriteLine(f()); Console.WriteLine("{0}{0}", Environment.NewLine); funcs.Clear();
for (var i = ; i < values.Count; i++)
{
//var v2 = values[i];
funcs.Add(() =>
{
var v2 = values[i]; //will throw exception
return v2;
});
} foreach (var f in funcs)
Console.WriteLine(f());
为什么要用闭包closure,另开一个专题学习。
Goto: [JS] This is ”Closure“