js高级
2020-05-07 10:47:48 0 举报
AI智能生成
js javascript 原型 变量提升 函数提升 闭包 对象 模块化 commonjs模块化 es6模块化 amd模块化
作者其他创作
大纲/内容
原型
每个函数都有一个prototype属性,这个属性是指向一个对象的引用,这个对象称为原型对象,原型对象包含函数实例共享的方法和属性,
也就是说将函数用作构造函数调用(使用new操作符调用)的时候,新创建的对象会从原型对象上继承属性和方法。
function Person() {
var name;
var age;
}
Person.prototype.setName = function (name) {
this.name = name;
}
var person = new Person();
person.setName('zhangsansan')
console.log(person.name) // zhangsansan
person.__proto__.setAge = function (age) {
this.age = age
}
var user = new Person();
user.setAge(22)
console.log(user.age) // 22
提升
变量提升
JavaScript 中,函数及变量的声明都将被提升到函数的最顶部。
JavaScript 中,变量可以在使用后声明,也就是变量可以先使用再声明。
x = 5; // 变量 x 设置为 5
var elem = document.getElementById("demo"); // 查找元素
elem.innerHTML = x; // 在元素中显示 x
var x; // 声明 x
变量初始化不会提升
var x = 5; // 初始化 x
var elem = document.getElementById("demo"); // 查找元素
elem.innerHTML = "x 为:" + x + ",y 为:" + y; // 显示 x 和 y
var y = 7; // 初始化 y
x 为:5,y 为:undefined
函数提升
函数提升只会提升函数声明,而不会提升函数表达式。
var a = 1;
function foo() {
a = 10;
console.log(a);
return;
function a() {};
}
foo();
console.log(a);
var a = 1; // 定义一个全局变量 a
function foo() {
// 首先提升函数声明function a () {}到函数作用域顶端, 然后function a () {}等同于 var a = function() {};最终形式如下
var a = function () {}; // 定义局部变量 a 并赋值。
a = 10; // 修改局部变量 a 的值,并不会影响全局变量 a
console.log(a); // 打印局部变量 a 的值:10
return;
}
foo();
console.log(a); // 打印全局变量 a 的值:1
闭包
闭包函数:声明在一个函数中的函数,叫做闭包函数。
闭包:内部函数总是可以访问其所在的外部函数中声明的参数和变量,即使在其外部函数被返回(寿命终结)了之后。
闭包就是可以创建一个独立的环境,每个闭包里面的环境都是独立的,互不干扰。闭包会发生内存泄漏,每次外部函数执行的时 候,外部函数的引用地址不同,都会重新创建一个新的地址。但凡是当前活动对象中有被内部子集引用的数据,那么这个时候,这个数据不删除,保留一根指针给内部活动对象。
function outerFn(){
var i = 0;
function innnerFn(){
i++;
console.log(i);
}
return innnerFn;
}
var inner1 = outerFn();
var inner2 = outerFn();
inner1(); // 1
inner2(); // 2
对象
工厂模式
function createPerson(name, age) {
var person = {
name: name,
age: age,
setName: function (name) {
this.name = name;
}
}
return person;
}
var user = createPerson('zhangsansan', 22)
console.log(user.name, user.age)
缺点:类型都为Object
构造函数模式
function Animal(name, age) {
this.name = name
this.age = age
this.setName = function (name) {
this.name = name;
}
}
var cat = new Animal('cat', 2)
console.log(cat instanceof Animal)
缺点:方法重复
构造函数+原型
function Animal(name, age) {
this.name = name
this.age = age
}
Animal.prototype.setName = function (name) {
this.name = name;
}
var cat = new Animal('cat', 2)
console.log(cat instanceof Animal)
继承
function Super() {
this.supProp = "supProp"
}
Super.prototype.showSupProp = function () {
console.log(this.supProp)
}
function Sub() {
this.subProp = "subProp"
}
Sub.prototype = new Super()
Sub.prototype.showSubProp = function () {
console.log(this.subProp)
}
var sub = new Sub()
sub.showSupProp()
sub.showSubProp()
sub.supProp = 'new supProp'
sub.showSupProp()
console.log(sub)
console.log(Super.prototype)
最终
模块化
common js
npm 的模块都是 JavaScript 语言写的,但浏览器用不了,因为不支持 CommonJS 格式。要想让浏览器用上这些模块,必须转换格式。
原理
var module = {
exports: {}
};
(function(module, exports) {
exports.multiply = function (n) { return n * 1000 };
}(module, module.exports))
var f = module.exports.multiply;
f(5) // 5000
module对象
module.exports属性
module.exports属性表示当前模块对外输出的接口,其他文件加载该模块,实际上就是读取module.exports变量
exports变量
node为每一个模块提供了一个exports变量(可以说是一个对象),指向 module.exports。这相当于每个模块中都有一句这样的命令
var exports = module.exports;
这样,在对外输出时,可以在这个变量上添加方法。例如
exports.add = function (r){return Math.PI * r *r};
注意:不能把exports直接指向一个值,这样就相当于切断了 exports 和module.exports 的关系。例如 exports=function(x){console.log(x)};
一个模块的对外接口,就是一个单一的值,不能使用exports输出,必须使用 module.exports输出。module.exports=function(x){console.log(x);};
require命令
equire命令用于加载模块文件,相当于读入并执行一个js文件,然后返回该模块的exports对象,没有发现指定模块,则就会报错。例如 example.js
exports.name = 'tom';
exports.age = 50;
在 同目录下的 demo.js 文件中
var example = require('./example.js');
console.log(example.name); // tom
console.log(example.age); // 50
或者 example.js
function fn(){console.log(1)};
var name = 'tom'
module.exports = {fn:fn,name:name}
这里可以简写一下,es6的对象简写,key 和 value 一致,可以只写一个。
module.exports = {fn,name};
在 同目录下的 demo.js 文件中 var example = require('./example.js');
example.fn(); // 1
console.log(example.name); // tom
模块的加载机制
commonsJS的加载机制,输入的是被输出值的拷贝。也就是说,一旦输出一个值,模块内部的变化就影响不到这个值了。
Subtopic 2
ES6模块化
在ES6中每一个模块即是一个文件,在文件中定义的变量,函数,对象在外部是无法获取的。如果你希望外部可以读取模块当中的内容,就必须使用export来对其进行暴露(输出)。
test.js
let myName="laowang";
let myAge=90;
let myfn=function(){
return "我是"+myName+"!今年"+myAge+"岁了"
}
export {
myName,
myAge,
myfn
}
index.js
import {myfn,myAge,myName} from "./test.js";
console.log(myfn());//我是laowang!今年90岁了
console.log(myAge);//90
console.log(myName);//laowang
如果你不想暴露模块当中的变量名字,可以通过as来进行操作
test.js
let myName="laowang";
let myAge=90;
let myfn=function(){
return "我是"+myName+"!今年"+myAge+"岁了"
}
export {
myName as name,
myAge as age,
myfn as fn
}
/******************************接收的代码调整为**********************/
import {fn,age,name} from "./test.js";
console.log(fn());//我是laowang!今年90岁了
console.log(age);//90
console.log(name);//laowang
默认导出:一个模块只能有一个默认导出,对于默认导出,导入的名称可以和导出的名称不一致。
/******************************导出**********************/
export default function(){
return "默认导出一个方法"
}
/******************************引入**********************/
import myFn from "./test.js";//注意这里默认导出不需要用{}。
console.log(myFn());//默认导出一个方法
可以将所有需要导出的变量放入一个对象中,然后通过default export进行导出
/******************************导出**********************/
export default {
myFn(){
return "默认导出一个方法"
},
myName:"laowang"
}
/******************************引入**********************/
import myObj from "./test.js";
console.log(myObj.myFn(),myObj.myName);//默认导出一个方法 laowang
重命名export和import:如果导入的多个文件中,变量名字相同,即会产生命名冲突的问题,为了解决该问题,ES6为提供了重命名的方法。
/******************************test1.js**********************/
export let myName="我来自test1.js";
/******************************test2.js**********************/
export let myName="我来自test2.js";
/******************************index.js**********************/
import {myName as name1} from "./test1.js";
import {myName as name2} from "./test2.js";
console.log(name1);//我来自test1.js
console.log(name2);//我来自test1.js
AMD模块化
AMD是"Asynchronous Module Definition"的缩写,意思就是"异步模块定义"
它采用异步方式加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。
AMD也采用require()语句加载模块,但是不同于CommonJS,它要求两个参数:
require([module], callback);
第一个参数[module],是一个数组,里面的成员就是要加载的模块;第二个参数callback,是加载成功之后的回调函数。
require(['math'], function (math) {
math.add(2, 3);
});
RequireJS
实现了AMD规范
假定我们自己的代码文件是main.js,也放在js目录下面。
<script src="js/require.js" data-main="js/main"></script>
模块的加载 main.js 开始部分
require.config({
baseUrl: "js/lib",
paths: {
"jquery": "jquery.min",
// 或者"jquery": "https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min"
"underscore": "underscore.min",
"backbone": "backbone.min"
}
});
require.js要求,每个模块是一个单独的js文件。这样的话,如果加载多个模块,就会发出多次HTTP请求,会影响网页的加载速度。因此,require.js提供了一个优化工具,当模块部署完毕以后,可以用这个工具将多个模块合并在一个文件中,减少HTTP请求数。
AMD模块的写法
假定现在有一个math.js文件,它定义了一个math模块。那么,math.js就要这样写:
// math.js
define(function () {
var add = function (x, y) {
return x + y;
};
return {
add: add
};
});
加载方法如下:
require(['math'], function (math){
alert(math.add(1,1));
});
如果这个模块还依赖其他模块,那么define()函数的第一个参数,必须是一个数组,指明该模块的依赖性。
define(['myLib'], function(myLib){
function foo(){
myLib.doSomething();
}
return {
foo : foo
};
});
加载非规范的模块
require.config()接受一个配置对象,这个对象除了有前面说过的paths属性之外,还有一个shim属性,专门用来配置不兼容的模块。具体来说,每个模块要定义(1)exports值(输出的变量名),表明这个模块外部调用时的名称;(2)deps数组,表明该模块的依赖性。
require.config({
shim: {
'underscore':{
exports: '_'
},
'backbone': {
deps: ['underscore', 'jquery'],
exports: 'Backbone'
}
}
});
shim: {
'jquery.scroll': {
deps: ['jquery'],
exports: 'jQuery.fn.scroll'
}
}
0 条评论
下一页