1、JavaScript编程语言准备

JavaScript编程语言准备

和b站黑马程序员6天实战游戏开发微信小程序学习

选择的游戏引擎是CocosCreator ,编程语言是JavaScript,游戏后台采用Java。

JavaScript的基本语言基础:

一、变量声明let与const

let 声明的变量只在 let 命令所在的代码块内有效。

const 声明一个只读的常量,一旦声明,常量的值就不能改变。

1、let的使用

1、只在代码块内有效

for (var a = 0; a < 10; a++) {}
console.log(a);
// 输出结果--> 10for (let b = 0; b < 10; b++) {}
console.log(b);
// 输出结果--> Uncaught ReferenceError: b is not defined

2、不能重复声明

var a = 1;
var a = 2;
console.log(a)
// 输出结果--> 2let b = 1;
let b = 2;
console.log(b)
// 输出结果--> Uncaught SyntaxError: Identifier 'b' has already been declared

2、const的使用

const 声明一个只读变量,声明之后不允许改变。意味着,一旦声明必须初始化,否则会报错

const PI = "3.1415926";
console.log(PI)
// 输出结果--> 3.1415926const GENDER; 
// 输出结果--> Uncaught SyntaxError: Missing initializer in const declaration

3、暂时性死区

ES6 明确规定,代码块内如果存在 let 或者 const,代码块会对这些命令声明的变量从块的开始就形成一个封闭作用域。代码块内,在声明变量 PI 之前使用它会报错。

var PI = "a";
if (true) {
    console.log(PI)
    // 输出结果--> a
}if(true){
    console.log(PI);
    // 输出结果--> Uncaught ReferenceError: Cannot access 'PI' before initialization
    const PI = "3.1415926";
}

二、基本数据类型Symbol

1、Symbol的基本用法

System具有唯一性,而且调用System()函数,可以传入一个字符串参数

let s1 = Symbol()
let s2 = Symbol('another symbol')
let s3 = Symbol('another symbol')
​
s1 === s2 // false
s2 === s3 // false
}

2、应用场景1:使用Symbol来作为对象属性名

const PROP_NAME = Symbol()
const PROP_AGE = Symbol()let obj = {
  [PROP_NAME]: "一斤代码"
}
obj[PROP_AGE] = 18
​
obj[PROP_NAME] // '一斤代码'
obj[PROP_AGE] // 18

随之而来的是另一个非常值得注意的问题:就是当使用了Symbol作为对象的属性key后,在对该对象进行key的枚举时,会有什么不同?在实际应用中,我们经常会需要使用Object.keys()或者for…in来枚举对象的属性名,那在这方面,Symbol类型的key表现的会有什么不同之处呢?来看以下示例代码:

let obj = {
   [Symbol('name')]: '一斤代码',
   age: 18,
   title: 'Engineer'
}
​
Object.keys(obj)   // ['age', 'title']for (let p in obj) {
   console.log(p)   // 分别会输出:'age' 和 'title'
}
​
Object.getOwnPropertyNames(obj)   // ['age', 'title']JSON.stringify(obj)  // {"age":18,"title":"Engineer"}

有一些专门针对Symbol的API,比如:

// 使用Object的API
Object.getOwnPropertySymbols(obj) // [Symbol(name)]// 使用新增的反射API
Reflect.ownKeys(obj) // [Symbol(name), 'age', 'title']

3、应用场景2:使用Symbol来替代常量

const TYPE_AUDIO = 'AUDIO'
const TYPE_VIDEO = 'VIDEO'
const TYPE_IMAGE = 'IMAGE'
// 使用Symbol来替代常量(Symbol的唯一性)
const TYPE_AUDIO = Symbol()
const TYPE_VIDEO = Symbol()
const TYPE_IMAGE = Symbol()

三、解构赋值

1、数组模型的解构

(1) 基本使用:

// 基本用法 a=1,b=2,c=3
let [a, b, c] = [1, 2, 3]// 可嵌套
let [a, [[b], c]] = [1, [[2], 3]];// 忽略
let [a, , b] = [1, 2, 3];// 剩余运算符
let [a, ...b] = [1, 2, 3];// 默认值
let [a = 3, b = a] = [];     // a = 3, b = 3
let [a = 3, b ] = [4,6];     // a = 4, b = 6
// 当解构模式有匹配结果,且匹配结果是 undefined 时,会触发默认值作为返回结果。
let [a = 2] = [undefined]; // a = 2

(2) 如果解构不成功,变量的值就等于undefined。

let [foo] = [];
let [bar, foo] = [1];

(3) 可遍历对象皆可解构

let [a, b, c, d, e] = 'hello';
// a = 'h'
// b = 'e'
// c = 'l'
// d = 'l'
// e = 'o'

2、对象模型的解构

// 基本
let { foo, bar } = { foo: 'aaa', bar: 'bbb' };
// foo = 'aaa'
// bar = 'bbb'
 
let { baz : foo } = { baz : 'ddd' };
// foo = 'ddd'//可嵌套可忽略
let obj = {p: ['hello', {y: 'world'}] };
let {p: [x, { y }] } = obj;
// x = 'hello'
// y = 'world'
let obj = {p: ['hello', {y: 'world'}] };
let {p: [x, {  }] } = obj;
// x = 'hello'//不完全解构
let obj = {p: [{y: 'world'}] };
let {p: [{ y }, x ] } = obj;
// x = undefined
// y = 'world'// 剩余运算符
let {a, b, ...rest} = {a: 10, b: 20, c: 30, d: 40};
// a = 10
// b = 20
// rest = {c: 30, d: 40}// 解构默认值
let {a = 10, b = 5} = {a: 3};
// a = 3; b = 5;
let {a: aa = 10, b: bb = 5} = {a: 3};
// aa = 3; bb = 5;

四、函数常见写法及常用函数Math

1、原生函数常见写法

命名函数
function f1(){
    console.log("这个函数就是命名函数");
}
匿名函数
function (){
    console.log("这个函数就是匿名函数");
}
//可以通过变量接受来调用, 把匿名函数赋给一个变量
var f2 = function(){
    console.log("把匿名函数赋给一个变量");
}f2();//f2中存储的就是函数代码,用变量名加()就是函数调用
自执行函数
//自执行函数或是自调用函数 声明完了,马上进行调用,只能使用一次。

// 格式一:(函数)(实参)
(function (n1,n2){
    console.log("这是匿名函数的自执行的第一种写法,结果为:"+(n1+n2))
})(10,100)  // -->110// 格式二:(函数(实参))
(function (n1,n2){
    console.log("这是匿名函数的自执行的第二种写法,结果为:"+(n1+n2))
}(10,100)) // -->110

2、箭头函数(ES6新特性)

基本使用
x => x * x

当箭头函数函数体有多行语句,用 {} 包裹起来,表示代码块,当只有一行语句,并且需要返回结果时,可以省略 {} , 结果会自动返回。

let f = (a,b) => {
 let result = a+b;
 return result;
}
f(1,2);  // 3
箭头函数返回对象写法

当箭头函数要返回对象的时候,为了区分于代码块,要用 () 将对象包裹起来

// 错误写法
let f = (id,name) => {id: id, name: name};
f(18,"播仔");  // SyntaxError: Unexpected token :
 
// 正确写法
let f = (id,name) => ({id: id, name: name});
f(18,"播仔");  // {id: 18, name: ‘播仔’}
箭头函数的this

1、普通函数的this:指向它的调用者,如果没有调用者则默认指向window。
2、箭头函数体中的 this 对象,是定义函数时的对象,而不是使用函数时的对象。
3、箭头函数中的this,首先从它的父级作用域中找,如果父级作用域还是箭头函数,再往上找,如此直至找到this的指向。

//这里只能用var定义变量,let,const定义的变量,不是绑定在window下。
var str = 'window';const obj = {
    str:'obj',
    nativeFn: function(){
        console.log(this.str, '当前词法作用域中的this');
        return function(){
            console.log('原生函数',this.str);
        }
    },
    arrowFn: function(){
        console.log(this.str, '当前词法作用域中的this');
        return ()=>{
            console.log('箭头函数',this.str);
        }
    }
};
const obj2 = {
    str:'obj2'
}var nativeFn = obj.nativeFn();
var arrowFn = obj.arrowFn();
​
console.log('函数调用一 **********');
nativeFn();
arrowFn();
​
console.log('函数调用二 **********');
nativeFn.call(obj2);
arrowFn.call(obj2);

3、Math函数在游戏中的使用

随机数

Math.random() 取[0,1)的随机小数

console.log(Math.random());
取整
// 向上取整
console.log(Math.ceil(12.03));//13
console.log(Math.ceil(-12.92));//-12
// 向下取整
console.log(Math.floor(12.3));//12
console.log(Math.floor(-12.9));//-13// 四舍五入
console.log(Math.round(16.3)) // 16
console.log(Math.round(16.5)) // 17
console.log(Math.round(-16.5))// -16
console.log(Math.round(-16.51))// -17
绝对值
console.log(Math.abs(-12)) // 12
圆周率和三角函数

假设我们现在做一个二维射击游戏,他的地图就是一个二维笛卡尔坐标,已知敌人和枪手的坐标点,要计算枪手射击角度,我们就需要用到三角函数相关的api

// 枪手
let a = [100,100]
// 敌人
let b = [200,200]
let x = Math.abs(a[0] - b[0])
let y = Math.abs(a[1] - b[1])
// 求斜边
let z = Math.sqrt(x * x + y * y)
// 计算射击角度
let angle = Math.asin(y / z) / Math.PI * 180
console.log(Math.round(angle))
幂计算和开方
Math.pow(10,2) = 100;
Math.sqrt(100) = 10;

五、Map&Set

1、Map 对象

Map 和 Object 的区别:
一个 Object 的键只能是字符串或者 Symbols,但一个 Map 的键可以是任意值。
Map 中的键值是有序的(FIFO 原则),而添加到对象中的键则不是。
Map 的键值对个数可以从 size 属性获取,而 Object 的键值对个数只能手动计算。

Map 的基本使用
// 普通字符串作为key值
let strMap = new Map();
strMap.set("str","key是String类型的值");
let str = strMap.get("str");
console.log(str)// 对象作为key值
let objMap = new Map();
let keyObj = {};
objMap.set(keyObj, "和键 keyObj 关联的值");
let obj = objMap.get(keyObj);
console.log(obj)
​
objMap.size // 1

2、Set 对象

Set 对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用。

和Array互相转换
// Array 转 Set
let mySet = new Set(["value1", "value2", "value3"]);
// 用...操作符,将 Set 转 Array
let myArray = [...mySet];// String 转 Set
let mySet = new Set('hello');  // Set(4) {"h", "e", "l", "o"}
Set 对象作用

数组去重

let mySet = new Set([1, 2, 3, 4, 4]); 
[...mySet]; // [1, 2, 3, 4]

并集

let a = new Set([1, 2, 3]); 
let b = new Set([4, 3, 2]);
let union = new Set([...a, ...b]); // {1, 2, 3, 4}

交集

let a = new Set([1, 2, 3]); 
let b = new Set([4, 3, 2]); 
let intersect = new Set([...a].filter(x => b.has(x))); // {2, 3}

差集

let a = new Set([1, 2, 3]); 
let b = new Set([4, 3, 2]); 
let difference = new Set([...a].filter(x => !b.has(x))); // {1}

六、面向对象编程

1、面向对象

ES6是支持class关键字的

//定义类
class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }toString() {
    return '(' + this.x + ', ' + this.y + ')';
  }
}

cocos creator 源码 CCClass

function CCClass (options) {
    options = options || {};var name = options.name;
    var base = options.extends/* || CCObject*/;
    var mixins = options.mixins;// create constructor
    var cls = define(name, base, mixins, options);
    if (!name) {
        name = cc.js.getClassName(cls);
    }
​
    cls._sealed = true;
    if (base) {
        base._sealed = false;
    }// define Properties
    var properties = options.properties;
    if (typeof properties === 'function' ||
        (base && base.__props__ === null) ||
        (mixins && mixins.some(function (x) {
            return x.__props__ === null;
        }))
    ) {
        if (CC_DEV && options.__ES6__) {
            cc.error('not yet implement deferred properties for ES6 Classes');
        }
        else {
            deferredInitializer.push({cls: cls, props: properties, mixins: mixins});
            cls.__props__ = cls.__values__ = null;
        }
    }
    else {
        declareProperties(cls, name, properties, base, options.mixins, options.__ES6__);
    }// define statics
    var statics = options.statics;
    if (statics) {
        var staticPropName;
        if (CC_DEV) {
            for (staticPropName in statics) {
                if (INVALID_STATICS_DEV.indexOf(staticPropName) !== -1) {
                    cc.errorID(3642, name, staticPropName,
                        staticPropName);
                }
            }
        }
        for (staticPropName in statics) {
            cls[staticPropName] = statics[staticPropName];
        }
    }// define functions
    for (var funcName in options) {
        if (BUILTIN_ENTRIES.indexOf(funcName) >= 0) {
            continue;
        }
        var func = options[funcName];
        if (!preprocess.validateMethodWithProps(func, funcName, name, cls, base)) {
            continue;
        }
        // use value to redefine some super method defined as getter
        js.value(cls.prototype, funcName, func, true, true);
    }
​
​
    var editor = options.editor;
    if (editor) {
        if (js.isChildClassOf(base, cc.Component)) {
            cc.Component._registerEditorProps(cls, editor);
        }
        else if (CC_DEV) {
            cc.warnID(3623, name);
        }
    }return cls;
}