JavaScript는 ECMAScript 표준에 따라 설계 및 구현되었습니다. 나중에 언급되는 JavaScript 구문은 실제로 ES5 표준을 구현한 것입니다.
먼저 기본 문법은 무엇일까요?
거의 모든 언어의 기본 구문은 데이터 유형, 연산자, 제어문 외에는 거의 차이가 없습니다. , 기능 등을 간략하게 나열하면 다음과 같습니다.
JavaScript에는 객체 유형인 1개의 복잡한 데이터 유형이 포함되어 있습니다. 객체 유형은 다른 모든 객체의 기본 클래스입니다. 참고: JavaScript는 부동 소수점 숫자와 정수를 구분하지 않으며 둘 다 숫자로 표시됩니다.
앞서 말씀드린 기본 데이터형 5가지와 여기서 복합 데이터형 1가지, 이게 다 데이터형이에요!
기본 연산자이건 상식입니다. 무슨 일이 일어나고 있는지 알아두세요.
일반적으로 사용되는 연산자에는 산술 연산자, 관계 연산자, 부울 연산자, 할당 연산자 등이 있습니다.
제어문이것은 if-else와 같은 제어문이라고 흔히 부르는 것입니다.
은 일반적으로 사용되지 않습니다: if 문, switch 문,
for 문,
while문, for-in 문. 함수함수는 이론적으로 논리의 작은 부분을 캡슐화한 것입니다.
JavaScript 기능은 다른 언어와 매우 다릅니다. JavaScript 함수는 매개변수와 반환값을 모두 사용할 수 있습니다. 또한 JavaScript 함수는 매개변수를 원하는 만큼 받아들일 수 있으며 이러한 매개변수는 인수 객체를 통해 액세스할 수 있습니다.
모든 언어의 기본 구문은 세부 사항의 일부 차이점을 제외하면 대략 위와 같습니다: 데이터 유형, 연산자, 제어 문, 함수, 모듈 등.
다음에서는 약간 더 복잡한 개념을 소개합니다.
변수, 범위, 메모리 문제 변수자바스크립트 변수는 기본형과 참조형 두 가지로 구분됩니다. 기본 유형은 앞서 언급한 5가지 기본 데이터 유형이고, 참조 유형은 앞서 언급한 객체(Object)와 이를 기반으로 하는 기타 복합 데이터 유형이다.
✦ 기본형: 메모리의 실제 크기를 차지합니다. 값을 할당하면 메모리에 새 복사본이 생성됩니다. 스택 메모리에 저장됩니다.
✦ 참조 유형: 객체 자체가 아닌 객체에 대한 포인터. 값을 할당할 때 객체를 가리키는 새 포인터가 생성됩니다. 힙 메모리에 저장됩니다.
가변 메모리 할당
한마디로 기본 유형은 메모리에 있는 실제 값이고 참조 유형은 메모리에 있는 포인터입니다. 객체를 가리키는 경우 여러 참조 유형이 동시에 동일한 객체를 가리킬 수 있습니다.
그럼 특정 변수가 어떤 데이터 유형인지 확인하는 방법은 무엇입니까?
변수가 어떤 기본 유형인지 확인하려면 typeof 연산자를 사용하세요.
변수가 어떤 참조 유형인지 확인하려면 instanceof 연산자를 사용하세요.
이 점을 잊지 마세요!
범위
변수는 특정 범위에서 선언되며, 범위에 따라 이러한 변수의 수명주기가 결정되며, 변수에 액세스할 수 있는 코드는 무엇입니까? 그 안에.
JavaScript 범위에는 전역 범위와 함수 범위만 포함되며 블록 수준 범위는 포함되지 않습니다!
스코프를 중첩하여 스코프 체인을 형성할 수 있습니다. 스코프 체인의 존재로 인해 변수 검색은 위쪽으로 추적될 수 있습니다. 즉, 자식 함수는 부모 함수의 범위 => 조상 함수의 범위 => 전역 범위까지 접근할 수 있습니다. 함수는 클로저라고도 하며 나중에 소개됩니다.
var color = "blue"; function changeColor() { var anotherColor = "red"; function swapColors() { var tempColor = anotherColor; anotherColor = color; color = tempColor; // 这里可以访问color、anotherColor、tempColor } // 这里可以访问color、anotherColor,但不能访问tempColor swapColors(); } // 这里只能访问color、changeColor();
아래 그림과 같이 각 스코프와 중첩 스코프에서 접근 가능한 변수를 위쪽으로 추적할 수 있습니다.
스코프 체인
스코프의 개념은 단순해 보이지만 실제 사용에는 문제가 많다면, 꼼꼼히 분석해야 합니다.
메모리 문제
JavaScript 엔진에는 자동 가비지 수집 메커니즘이 있으므로 메모리 할당 및 가비지 수집 문제에 너무 많은 주의를 기울일 필요가 없습니다. 여기서는 더 이상 설명하지 않겠습니다!
앞서 언급했듯이 Object는 유일한 복합 데이터 유형이며 참조 유형은 Object 유형에서 상속됩니다.
✦ Array: 배열 유형
✦ Date: 날짜 유형
✦ RegExp: 정규식입력하세요. 이에 대해 자세히 알아보면 도움이 됩니다!
✦ 等等...
那问题来了,我们用的最多的函数是什么数据类型呢?答案是Function类型!
诶,好像发现了点什么东西?由于Function是引用类型,而JavaScript又可以往引用类型上加属性和方法。那么,函数也可以!这也是JavaScript函数强大和复杂的地方。也就是说:函数也可以拥有自定义方法和属性!
此外,JavaScript对前面提到的5种基本类型的其中3种也做了引用类型封装,分别是Boolean、Number、String,但其实使用不多,了解就行。
对了,在所有代码执行之前,作用域就内置了两个对象,分别是Global和Math,其中浏览器的Global就是window啦!
到此为止,JavaScript中基础的概念都差不多介绍了,其中函数和作用域相对来说复杂一些,其他的都比较浅显。
接下来,我会介绍介绍JavaScript中一些稍微复杂一些的概念:面向对象。
JavaScript本身并没有类和接口的概念了,面向对象都是基于原型实现的。
为了简单,我们只分析面向对象的两个问题:
✦ 如何定义一个类?
✦ 如何实现类的继承
定义一个类
不扯其他的,直接告诉你。我们使用构造函数+原型的方式来定义一个类。
使用构造函数创建自定义类型,然后使用new操作符来创建类的实例,但是构造函数上的方法和属性在每个示例上都存在,不能共享,于是我们引入原型来实现方法和属性的共享。
原型
最后,我们将需要共享的方法和属性定义在原型上,把专属于实例的方法和属性放到构造函数中。到这儿,我们就通过构造函数+原型的方式定义了一个类。
// 构造函数 function Person(name, age, job) { this.name = name; this.age = age; this.job = job; this.friends = ["Shelby", "Court"]; } // 原型 Person.prototype = { constructor: Person, sayName: function() { return this.name; } } // 实例化 var person1 = new Person("Nicholas", 29, "Software Engineer"); var person2 = new Person("Greg", 27, "Doctor"); person1.friends.push("Van"); alert(person1.friends); //输出"Shelby,Count,Van" alert(person2.friends); //输出"Shelby,Count" alert(person1.friends === person2.friends); //输出false alert(person1.sayName === person2.sayName); //输出true
实现继承
前文讲了如何定义一个类,那么我们定义一个父类,一个子类。
如何让子类继承父类呢?不扯别的,直接告诉你。JavaScript通过原型链来实现继承!
如何构建原型链呢?将子类实例赋值给父类构造函数的原型即可。好绕,但是千万得记住了!
原型链继承
构建原型链之后,子类就可以访问父类的所有属性和方法!
// 父类 function SuperType() { this.property = true; } SuperType.prototype.getSuperValue = function() { return this.property; }; // 子类 function SubType() { this.subproperty = false; } //子类继承父类 SubType.prototype = new SuperType(); //给子类添加新方法 SubType.prototype.getSubValue = function() { return this.subproperty; }; //重写父类的方法 SubType.prototype.getSuperValue = function() { return false; }; // 实例化 var instance = new SubType(); console.log(instance.getSuperValue()); //输出false
面向对象的知识可以用一本书来写,这儿只是简单的介绍下最基础最常用的概念。
JavaScript中有两种定义函数的方式:函数声明和函数表达式。
使用函数表达式无须对函数命名,从而实现动态编程,也即匿名函数。有了匿名函数,JavaScript函数有了更强大的用处。
递归是一种很常见的算法,经典例子就是斐波拉契数列。也不扯其他的,直接说递归的最佳实践,上代码:
// 最佳实践,函数表达式 var factorial = (function f(num) { if (num <= 1) { return 1; } else { return num * f(num - 1); } }); // 缺点: // factorial存在被修改的可能 // 导致 return num * factorial(num - 1) 报错 function factorial(num) { if (num <= 1) { return 1; } else { return num * factorial(num - 1); } } // 缺点: // arguments.callee,规范已经不推荐使用 function factorial(num) { if (num <= 1) { return 1; } else { return num * arguments.callee(num - 1); } }
递归就是这样,好多人还在使用arguments.callee的方式,改回函数表达式的方式吧,这才是最佳实践。
啰嗦一句,好多人觉得递归难写,其实你将其分为两个步骤就会清晰很多了。
✦ 边界条件,通常是if-else。
✦ 递归调用。
按这个模式,找几个经典的递归练练手,就熟悉了。
闭包
很多人经常觉得闭包很复杂,很容易掉到坑里,其实不然。
那么闭包是什么呢?如果一个函数可以访问另一个函数作用域中的变量,那么前者就是闭包。自然,创建闭包的常用方式就是在一个函数内部创建另一个函数!
并没有什么神奇的,这就是闭包,闭包就是一个理所当然的现象,也就是在父函数中定义子函数,然后子函数可以访问父函数的作用域。
我们通常是因为被闭包坑了,才会被闭包吓到,尤其是面试题里一堆闭包。
闭包的定义前面提了,如何创建闭包也说了,那么我们说说闭包的缺陷以及如何解决?
/* 我们通过subFuncs返回函数数组,然后分别调用执行 */ // 返回函数的数组subFuncs,而这些函数对superFunc的变量有引用 // 这就是一个典型的闭包 // 那么有什么问题呢? // 当我们回头执行subFuncs中的函数的时候,我们得到的i其实一直都是10,为什么? // 因为当我们返回subFuncs之后,superFunc中的i=10 // 所以当执行subFuncs中的函数的时候,输出i都为10。 // // 以上,就是闭包最大的坑,一句话理解就是: // 子函数对父函数变量的引用,是父函数运行结束之后的变量的状态 function superFunc() { var subFuncs = new Array(); for (var i = 0; i < 10; i++) { subFuncs[i] = function() { return i; }; } return subFuncs; } // 那么,如何解决上诉的闭包坑呢? // 其实原理很简单,既然闭包坑的本质是:子函数对父函数变量的引用,是父函数运行结束之后的变量的状态 // 那么我们解决这个问题的方式就是:子函数对父函数变量的引用,使用运行时的状态 // 如何做呢? // 在函数表达式的基础上,加上自执行即可。 function superFunc() { var subFuncs = new Array(); for (var i = 0; i < 10; i++) { subFuncs[i] = function(num) { return function() { return num; }; }(i); } return subFuncs; }
综上,闭包本身不是什么复杂的机制,就是子函数可以访问父函数的作用域。
而由于JavaScript函数的特殊性,我们可以返回函数,如果我们将作为闭包的函数返回,那么该函数引用的父函数变量是父函数运行结束之后的状态,而不是运行时的状态,这便是闭包最大的坑。而为了解决这个坑,我们常用的方式就是让函数表达式自执行。
此外,由于闭包引用了祖先函数的作用域,所以滥用闭包会有内存问题。
好像把闭包说得一无是处,那么闭包有什么用处呢?
主要是封装吧...
封装
闭包可以封装私有变量或者封装块级作用域。
➙ 封装块级作用域
JavaScript并没有块级作用域的概念,只有全局作用域和函数作用域,那么如果想要创建块级作用域的话,我们可以通过闭包来模拟。
创建并立即调用一个函数,就可以封装一个块级作用域。该函数可以立即执行其中的代码,内部变量执行结束就会被立即销毁。
function outputNumbers(count) { // 在函数作用域下,利用闭包封装块级作用域 // 这样的话,i在外部不可用,便有了类似块级作用域 (function() { for (var i = 0; i < count; i++) { alert(i); } })(); alert(i); //导致一个错误! } // 在全局作用域下,利用闭包封装块级作用域 // 这样的话,代码块不会对全局作用域造成污染 (function() { var now = new Date(); if (now.getMonth() == 0 && now.getDate() == 1) { alert("Happy new year!"); } })(); // 是的,封装块级作用域的核心就是这个:函数表达式 + 自执行! (function() { //这里是块级作用域 })();
➙ 封装私有变量
JavaScript也没有私有变量的概念,我们也可以使用闭包来实现公有方法,通过隐藏变量暴露方法的方式来实现封装私有变量。
(function() { //私有变量和私有函数 var privateVariable = 10; function privateFunction() { return false; } //构造函数 MyObject = function() {}; //公有/特权方法 MyObject.prototype.publicMethod = function() { privateVariable++; return privateFunction(); }; })();
这差不多就是JavaScript的一些基础语法和稍微高级一些的用法,其实所谓的高级,都是JavaScript“不太成熟”的表现,尤其是面向对象,出于工程化的需要但是JavaScript本身并不完美支持。好在ES6最新标准解决了很多问题,结合Babel用起来也不用太考虑兼容性问题,如果你是新手的话,建议你直接去撸ES6+Babel吧。
✦ JavaScript的基础主要包括:5中基本数据类型、1种复杂的数据类型、操作符、控制语句、函数等。
✦ 了解基本的语法后,你还需要学习学习JavaScript的变量、作用域、作用域链。
✦ 常见的引用类型可以边查边用。作为过来人,建议多学学正则,对你的代码功底会有较大的提升。
✦ 面向对象编程的部分外面有很多种方式,你只需要记住使用构造函数+原型去定义一个类,使用原型链去实现继承即可。更多的扩展,去翻翻书吧。
✦ 函数表达式引出了几个比较好玩的东西:递归、闭包、封装。记住递归的最佳实践、闭包的定义及缺陷、闭包的适用场景。
JavaScript作为一门动态语言,和其他语言有较大的差异,这也造成很多人学习JavaScript时会觉得难学。但你现在看看前文,虽然是一个简略的总结,但JavaScript主要的内容就这些了,所以不要被自己吓到了。
위 내용은 일반적으로 사용되는 JavaScript 지식 포인트 모음의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!