이 글에서는 일반적으로 사용되는 JS 객체, 기본 데이터 구조, 기능적 기능 등 JavaScript의 일반적인 기능과 일반적으로 사용되는 디자인 패턴을 주로 요약합니다.
디렉토리:
우리 모두 알고 있듯이 JavaScript는 다음과 같은 효과를 얻을 수 있는 동적 객체 지향 프로그래밍 언어입니다.
1. JavaScript의 데이터 유형
JavaScript는 문자열, 숫자 및 부울이라는 세 가지 메타데이터 유형을 제공합니다. typeof(v)를 사용하여 변수 V의 유형인 typeof(v)===를 테스트할 수 있습니다. "번호"
5가지 기본 참조 유형인 객체, 배열, 함수, 날짜 및 RegExp를 제공합니다. 배열, 함수, 날짜 및 정규식은 특수 유형이지만 엄밀히 말하면 날짜 및 정규식은 다른 개체에 캡슐화될 수 있는 메타데이터 유형입니다.
JS에서는 변수형, 배열요소형, 함수 매개변수, 반환값형 등을 선언할 필요가 없으며, 유형 간 변환이 자동으로 수행됩니다.
변수 값은 다음과 같습니다.
string은 일련의 유니코드 문자열로, "hello world", 'A3FO' 또는 빈 문자열 ""과 같은 문자열입니다. 문자열 연결은 + 연산자를 통해 수행되거나 = 기호를 사용하여 두 문자를 확인할 수 있습니다. 문자열이 동일한지 여부
if (firstName + lastName === "James Bond") ...
숫자는 64비트 부동 소수점 숫자를 나타냅니다. JS에서는 정수와 부동 소수점 숫자 사이에 명확한 구분이 없습니다. 표현식의 값이 특정 숫자와 같지 않으면 해당 값을 NaN으로 설정할 수 있습니다. 이는 숫자가 아니며 결합될 수 있음을 의미합니다.
다음 표는 자세한 유형 테스트 및 변환입니다
2. 변수 범위
현재 JavaScript와 ES5는 전역 변수와 함수 범위라는 두 가지 범위 유형을 제공하며 블록 범위는 없습니다. 블록 범위의 범위가 불분명하므로 사용을 피해야 합니다. 다음 코드는 개발자들이 흔히 사용하는 패턴이지만 함정입니다.
function foo() { for (var i=0; i < 10; i++) { ... // do something with i } }
모든 변수는 함수 시작 부분에 선언하는 것이 가장 좋습니다. 블록 범위는 JS 및 ES6 버전에서 지원되며 변수는 키워드 let을 사용하여 정의됩니다.
엄격 모드
ES5부터는 런타임 오류를 감지하기 위해 엄격 모드가 사용됩니다. 엄격 모드에서는 선언되지 않은 변수에 값이 할당되면 예외가 발생합니다.
JavaScript 파일 또는 2934a685527f5cd6bcb20a3dc28499e1 요소 내에서 다음 코드를 입력하여 엄격 모드로 전환하세요.
엄격하게 사용하세요.
프로젝트가 의존하는 라이브러리가 엄격 모드와 호환되지 않는 한 엄격 모드를 사용하는 것이 좋습니다.
여러 개체
JS의 객체 개념은 OO 또는 UML의 객체와 다릅니다. 특히 JS에서는 객체를 인스턴스화할 필요가 없으며 속성 슬롯뿐만 아니라 메소드 슬롯을 포함하여 자체 메소드를 가질 수도 있습니다. . 또한 키-값 슬롯도 포함하므로 총 3가지 종류의 슬롯이 있는 반면, 일반 객체에는 속성 슬롯만 있습니다.
JS 객체는 일련의 이름-값으로 구성된 Slot입니다. 이름은 속성 이름, 함수 이름 또는 매핑 이름일 수 있습니다. 클래스를 인스턴스화하지 않고도 JSON(JS Object Declaration Syntax)을 사용하여 특정 방식으로 객체를 생성할 수 있습니다. 코드는 다음과 같습니다.
var person1 = { lastName:"Smith", firstName:"Tom"}; var o1 = Object.create( null); // an empty object with no slots
如果Slot 的name 是合法的JS 标识符,则Slot可表示属性,方法,或键值对。如果名称含有一些特殊字符如空格,则Slot代表键值对,是一种映射元素,如下:
Property Slot 中的name:
1. data value 属性,在这种情况下,Value 表示变量值,或是值表达式。
2. object-valued 属性,Value 表示对象的引用或对象表达式。
method Slot 表示的是JS 函数,它的值是JS 函数定义表达式:
Object 属性可以使用两种方法来访问:
1. 使用”.”(与C++ 和Java的类似):
person1.lastName = "Smith"
2. 使用map:
person1["lastName"] = "Smith"
JS 对象能够用于各种途径,以下是五种常见情况:
1. Record是Property slots集合,如:
var myRecord = {firstName:"Tom", lastName:"Smith", age:26}
2. map 如Hash map,数组,hash表;
var numeral2number = {"one":"1", "two":"2", "three":"3"}
3. Untyped 对象不需要实例化类,它可能包含property slot 和function slots:
var person1 = { lastName: "Smith", firstName: "Tom", getFullName: function () { return this.firstName +" "+ this.lastName; } }; Array List
JS array 即逻辑数据结构,通过数组下标访问。如数组初始化:
var a = [1,2,3];
JS数组可动态增长,因此数组索引有可能比实际元素个数多,如下:
a[4] = 7;
数组循环:
for (i=0; i deb559b41c3b2d69490ed3128dfca121 -1) ...
循环:
var i=0; for (i=0; i < a.length; i++) { console.log( a[i]); }
如果数组较小,可使用foreach 循环:
a.forEach( function (elem) { console.log( elem); })
JS 也提供克隆数组的函数:
var clone = a.slice(0);
三、Maps
map 提供key 到值得映射。JS map 是一串字符集,可包含空格:
var myTranslation = { "my house": "mein Haus", "my boat": "mein Boot", "my horse": "mein Pferd" }
增加:
myTranslation["my car"] = "mein Auto";
删除:
myTranslation["my car"] = "mein Auto";
查找:
if ("my bike" in myTranslation) ...
循环:
var i=0, key="", keys=[];
keys = Object.keys( m);
for (i=0; i < keys.length; i++) {
key = keys[i];
console.log( m[key]);
}
如果map 较小可使用foreach 语句:
Object.keys( m).forEach( function (key) {
console.log( m[key]);
})
复制map
var clone = JSON.parse( JSON.stringify( m))
小结:JavaScript 支持4种基本的数据结构。
1:array lists:如["one","two","three"],special JS对象
2:records:特殊的JS 对象,如{firstName:"Tom", lastName:"Smith"};
3:maps: 如{"one":1, "two":2, "three":3}
4:entity 表:如下表所示,是一种特殊的map,值是有固定ID的记录。
record,map,entity 在实际应用中没有明显的区分,只是概念上的区分。对JS 引擎而言,都是对象。但是从概念上是有区分的。
四、函数
如表1 所示,函数是特殊的JS 对象,有name 属性和length属性表示参数的个数,如下,判断v是否指向函数:
if (typeof( v) === "function") {...}
函数定义:
var myFunction = function theNameOfMyFunction () {...}
theNameOfMyFunction 是可选的,如果省略函数名称,则称该函数为匿名函数。通常,函数是通过变量来调用的,如上面的代码,则表示
myFunction 会调用myFunction()函数,而不是使用theNameOfMyFunction()调用。
匿名函数表达式在其他编程语言中称为lambda 表达式。如下代码是匿名函数。可以与预定义的sort函数对比:
var list = [[1,2],[1,3],[1,1],[2,1]]; list.sort( function (x,y) { return ((x[0] === y[0]) ? x[1]-y[1] : x[0]-y[0]); });
函数声明:
function theNameOfMyFunction () {...}
与代码
var theNameOfMyFunction = function theNameOfMyFunction () {...}
等价;
声明了函数theNameOfMyFunction ,并使用theNameOfMyFunction 变量来引用函数。
JS 提供函数内联,closure机制允许JS 函数调用函数之外的使用变量。函数可以创建closure 来存储当前的环境。如下,不需要通过参数将外部变量的结果传给内部函数,它本身就是可用的。
var sum = function (numbers) { var result = 0; numbers.forEach( function (n) { result += n; }); return result; }; console.log( sum([1,2,3,4]));
当执行一个方法时,可以使用内置arguments 对象访问函数内的参数,arguments 对象与数组使用方法类似,有长度属性,也有索引,并且可以使用For语句来循环迭代。但是由于它并不是Array 实例,因此JS arrary的部分方法无法应用如foreach。
arguments 对象的元素个数与函数参数个数相同,也可以定义方法的时候不指定参数个数,在调用时,可为函数赋多个参数,如:
var sum = function () { var result = 0, i=0; for (i=0; i < arguments.length; i++) { result = result + arguments[i]; } return result; }; console.log( sum(0,1,1,2,3,5,8)); // 20
方法是在构造函数的原型上定义的,可以通过对象创建的构造器调用,如Array.prototype.forEach;Array表示构造器,调用类的实例作为上下文对象参考的,如下: 在foreach中numbers表示上下文对象:
var numbers = [1,2,3]; // create an instance of Array numbers.forEach( function (n) { console.log( n); });
无论原型方法是否被上下文对象调用,但是只要是参数为对象,可以使用JS函数的Call 方法来辅助调用对象。如下,我们可以使用foreach 循环方法:
var sum = function () { var result = 0; Array.prototype.forEach.call( arguments, function (n) { result = result + n; }); return result; };
Function.prototype.call方法和apply都是为了改变某个函数运行时的 context 即上下文而存在的。
五、定义和使用类
类是面向对象中的基本概念,对象的实例化称为类。JS中定义类需要满足以下五个需求:
1.指定类的名称,实例级别的属性和方法,类级别的属性和方法。
2. 是可预测的实力,能够用于验证是否是某一对象的实例。
3. 实例级别的属性用于检测对象的直接类型。
4. 属性继承
5.方法继承。
除此之外还支持对哦集成和多分类。
JS中对类没有统一的定义规范,可以使用不同代码模式来定义类,并应用于多种不同的框架。JS中最常用的定义类的方法如下:
1. 构造函数规范,可通过prototype chain实现方法继承,并支持创建新的类实例。
2. factory 对象,用于预定义Object.create 方法来创建新实例。该方法中基于构造函数的继承机制可以被其他机制代替。
创建App都需要使用类,因此经常需要定义类之间的关系,所以必须保证,使用相同类
六、Constructor-based classes
只有ES6引入了定义基于构造器的类。新语法支持定义一些简单的类继承,具体步骤如下:
Step 1.a 基类Person 具有两个属性,first Name 和Last Name,实例层的方法toString和静态方法checkLastName;
class Person { constructor( first, last) { this.firstName = first; this.lastName = last; } toString() { return this.firstName + " " + this.lastName; } static checkLastName( ln) { if (typeof(ln)!=="string" || ln.trim()==="") { console.log("Error: " + "invalid last name!"); } } }
Step 1.b 类层次的属性定义:
Person.instances = {};
在第二步中,会定义一个带有其他属性和方法的子类,也有可能重写父类的相关方法:
class Student extends Person { constructor( first, last, studNo) { super.constructor( first, last); this.studNo = studNo; } // method overrides superclass method toString() { return super.toString() + "(" + this.studNo +")"; } }
ES5中,可以定义继承基于构造器类的子类。如下:
Step1.a 首先定义构造函数,能够隐式的定义类的属性并赋值;
function Person( first, last) { this.firstName = first; this.lastName = last; }
注意,上述代码中的this 指的是新生成的对象,当构造函数被调用时,该对象就已经生成了。
Step1.b 定义实例层的方法:
Person.prototype.toString = function () { return this.firstName + " " + this.lastName; }
Step 1.c 定义静态方法:
Person.checkLastName = function (ln) { if (typeof(ln)!=="string" || ln.trim()==="") { console.log("Error: invalid last name!"); } }
Step 1.d 定义类层次的静态属性
Person.instances = {};
Step 2.a 定义子类:
1: function Student( first, last, studNo) {
2: // invoke superclass constructor
3: Person.call( this, first, last);
4: // define and assign additional properties
5: this.studNo = studNo;
6: }
通过调用超类的构造函数Person.call( this, ...),来创建新对象。其中This指的是Student,Property Slots 在超类的构造函数中已经创建((firstName 和lastName) 以及其他子类相关的属性。在这种情况下可使用Property Inheritance 机制保证所有的属性已经被定义且被创建。
Step2b,通过构造函数的prototype 属性安装method inheritance 。如下,分配了一个新对象创建子类型构造函数的Prototype 属性,并做出适当的调整:
// Student inherits from Person Student.prototype = Object.create( Person.prototype); // adjust the subtype's constructor property Student.prototype.constructor = Student;
Step2c, 重新定义子类方法重写超类方法:
Student.prototype.toString = function () { return Person.prototype.toString.call( this) + "(" + this.studNo + ")"; };
基于构造器类的实例化是通过应用new 操作符来创建的,并提供合适的构造参数:
var pers1 = new Person("Tom","Smith");
方法toString 通过pers1. 来调用:
alert("The full name of the person are: " + pers1.toString());
在JS中,prototype 是具有method slots 的对象,可以通过JS方法或属性槽继承的。
七、基于Factory 的类
在该方法中定义了JS 对象Person,含有特殊的Create 方法来调用预定义的Object.Create方法,创建Person类型的对象;
var Person = { name: "Person", properties: { firstName: {range:"NonEmptyString", label:"First name", writable: true, enumerable: true}, lastName: {range:"NonEmptyString", label:"Last name", writable: true, enumerable: true} }, methods: { getFullName: function () { return this.firstName +" "+ this.lastName; } }, create: function (slots) { // create object var obj = Object.create( this.methods, this.properties); // add special property for *direct type* of object Object.defineProperty( obj, "type", {value: this, writable: false, enumerable: true}); // initialize object Object.keys( slots).forEach( function (prop) { if (prop in this.properties) obj[prop] = slots[prop]; }) return obj; } };
JS 개체 Person은 실제로 팩토리 기반 클래스를 나타냅니다. 팩터리 기반 클래스는 자체 Create 메서드를 호출하여 인스턴스화됩니다.
var pers1 = Person.create( {firstName:"Tom", lastName:"Smith"});
getFullName 메소드는 다음과 같이 pers1.을 통해 호출됩니다.
alert("그 사람의 전체 이름은 다음과 같습니다: " + pers1.getFullName());
각 속성의 선언은 위의 다섯 번째 줄에 표시된 대로 'descriptors'writable: true 및 enumerable: true라는 세 개의 매개변수와 값을 포함하는 Object.Create를 사용하여 선언됩니다.
정말 좋은 글이 아닌가요? 주저하지 말고 즐겨찾기에 추가해 보세요!