TS项目实战二:网页计算器

使用ts实现网页计算器工具,实现计算器相关功能,使用tsify进行项目编译,引入Browserify实现web界面中直接使用模块加载服务。

源码下载:点击下载

  1. 讲解视频

    TS实战项目四:计算器项目创建

    TS实战项目五:Browserify安装配置

  2. B站视频

    TS实战项目四:计算器项目创建

    TS实战项目五:Browserify安装配置

  3. 西瓜视频
    https://www.ixigua.com/7329331349171470899

一.知识点

1. tsify编译
2. tsconfig.json配置项
3. 模块定义及导入导出
4. 类定义
5. 参数属性
6. 存取器
7. 接口定义
8. 命名空间
9. 函数重载
10. 事件处理

二.效果预览

三.实现思路

使用ui和逻辑控制分离的模式,实现ui绘制及计算数据的单独处理,自定义按钮、输入框等ui组件,并绘制到界面中;通过事件监听的方式实现按钮点击后对应的逻辑控制,入结果计算、结果展示等。

四.创建项目

1. 创建node项目,使用npm init命令,如下:

2. 安装ts库,npm install typescript --save:

3. .\node_modules.bin\tsc --init生成ts的项目配置文件,此处注意直接用vscode的powershell运行的时候会保存,请切换到cmd命令窗口执行命令:

4. 安装lite-server库,npm install lite-server,安装完毕后添加"start": "lite-server"指令,用于提供web服务:

5. 安装Browserify,npm install tsify,提供浏览器环境中进行模块加载器的相关支持,具体文档参见:https://github.com/smrq/tsify,可创建bs-config.js文件进行web服务的配置。

6. 安装后创建build.js文件,用于进行ts代码的编译处理:

7. 在package.json中添加编译指令:

javascript 复制代码
"build-cli": "browserify -p tsify ./src/index.ts > ./dist/index.js",
"build-script": "node ./build.js > ./dist/index.js",

8. 创建dist文件夹,并创建index.html文件,实现web界面:

9. 创建后项目目录结构如下:

五.编码实现

1. tsconfig.json

javascript 复制代码
{
	"compilerOptions": {
		/* Visit https://aka.ms/tsconfig to read more about this file */
		/* Projects */
		// "incremental": true,                              /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
		// "composite": true,                                /* Enable constraints that allow a TypeScript project to be used with project references. */
		// "tsBuildInfoFile": "./.tsbuildinfo",              /* Specify the path to .tsbuildinfo incremental compilation file. */
		// "disableSourceOfProjectReferenceRedirect": true,  /* Disable preferring source files instead of declaration files when referencing composite projects. */
		// "disableSolutionSearching": true,                 /* Opt a project out of multi-project reference checking when editing. */
		// "disableReferencedProjectLoad": true,             /* Reduce the number of projects loaded automatically by TypeScript. */
		/* Language and Environment */
		"target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
			// "lib": [],                                        /* Specify a set of bundled library declaration files that describe the target runtime environment. */
			// "jsx": "preserve",                                /* Specify what JSX code is generated. */
			// "experimentalDecorators": true,                   /* Enable experimental support for legacy experimental decorators. */
			// "emitDecoratorMetadata": true,                    /* Emit design-type metadata for decorated declarations in source files. */
			// "jsxFactory": "",                                 /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
			// "jsxFragmentFactory": "",                         /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
			// "jsxImportSource": "",                            /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
			// "reactNamespace": "",                             /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
			// "noLib": true,                                    /* Disable including any library files, including the default lib.d.ts. */
			// "useDefineForClassFields": true,                  /* Emit ECMAScript-standard-compliant class fields. */
			// "moduleDetection": "auto",                        /* Control what method is used to detect module-format JS files. */
			/* Modules */
			"module": "commonjs", /* Specify what module code is generated. */
			"rootDir": "./", /* Specify the root folder within your source files. */
			// "moduleResolution": "node10",                     /* Specify how TypeScript looks up a file from a given module specifier. */
			// "baseUrl": "./",                                  /* Specify the base directory to resolve non-relative module names. */
			// "paths": {},                                      /* Specify a set of entries that re-map imports to additional lookup locations. */
			// "rootDirs": [],                                   /* Allow multiple folders to be treated as one when resolving modules. */
			// "typeRoots": [],                                  /* Specify multiple folders that act like './node_modules/@types'. */
			// "types": [],                                      /* Specify type package names to be included without being referenced in a source file. */
			// "allowUmdGlobalAccess": true,                     /* Allow accessing UMD globals from modules. */
			// "moduleSuffixes": [],                             /* List of file name suffixes to search when resolving a module. */
			// "allowImportingTsExtensions": true,               /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
			// "resolvePackageJsonExports": true,                /* Use the package.json 'exports' field when resolving package imports. */
			// "resolvePackageJsonImports": true,                /* Use the package.json 'imports' field when resolving imports. */
			// "customConditions": [],                           /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
			// "resolveJsonModule": true,                        /* Enable importing .json files. */
			// "allowArbitraryExtensions": true,                 /* Enable importing files with any extension, provided a declaration file is present. */
			// "noResolve": true,                                /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
			/* JavaScript Support */
			// "allowJs": true,                                  /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
			// "checkJs": true,                                  /* Enable error reporting in type-checked JavaScript files. */
			// "maxNodeModuleJsDepth": 1,                        /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
			/* Emit */
			// "declaration": true,                              /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
			// "declarationMap": true,                           /* Create sourcemaps for d.ts files. */
			// "emitDeclarationOnly": true,                      /* Only output d.ts files and not JavaScript files. */
			"sourceMap": true, /* Create source map files for emitted JavaScript files. */
			// "inlineSourceMap": true,                          /* Include sourcemap files inside the emitted JavaScript. */
			// "outFile": "./",                                  /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
			"outDir": "./dist", /* Specify an output folder for all emitted files. */
			// "removeComments": true,                           /* Disable emitting comments. */
			// "noEmit": true,                                   /* Disable emitting files from a compilation. */
			// "importHelpers": true,                            /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
			// "importsNotUsedAsValues": "remove",               /* Specify emit/checking behavior for imports that are only used for types. */
			// "downlevelIteration": true,                       /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
			// "sourceRoot": "",                                 /* Specify the root path for debuggers to find the reference source code. */
			// "mapRoot": "",                                    /* Specify the location where debugger should locate map files instead of generated locations. */
			// "inlineSources": true,                            /* Include source code in the sourcemaps inside the emitted JavaScript. */
			// "emitBOM": true,                                  /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
			// "newLine": "crlf",                                /* Set the newline character for emitting files. */
			// "stripInternal": true,                            /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
			// "noEmitHelpers": true,                            /* Disable generating custom helper functions like '__extends' in compiled output. */
			// "noEmitOnError": true,                            /* Disable emitting files if any type checking errors are reported. */
			// "preserveConstEnums": true,                       /* Disable erasing 'const enum' declarations in generated code. */
			// "declarationDir": "./",                           /* Specify the output directory for generated declaration files. */
			// "preserveValueImports": true,                     /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
			/* Interop Constraints */
			// "isolatedModules": true,                          /* Ensure that each file can be safely transpiled without relying on other imports. */
			// "verbatimModuleSyntax": true,                     /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
			// "allowSyntheticDefaultImports": true,             /* Allow 'import x from y' when a module doesn't have a default export. */
			"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
			// "preserveSymlinks": true,                         /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
			"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
			/* Type Checking */
			"strict": true, /* Enable all strict type-checking options. */
			// "noImplicitAny": true,                            /* Enable error reporting for expressions and declarations with an implied 'any' type. */
			// "strictNullChecks": true,                         /* When type checking, take into account 'null' and 'undefined'. */
			// "strictFunctionTypes": true,                      /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
			// "strictBindCallApply": true,                      /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
			// "strictPropertyInitialization": true,             /* Check for class properties that are declared but not set in the constructor. */
			// "noImplicitThis": true,                           /* Enable error reporting when 'this' is given the type 'any'. */
			// "useUnknownInCatchVariables": true,               /* Default catch clause variables as 'unknown' instead of 'any'. */
			// "alwaysStrict": true,                             /* Ensure 'use strict' is always emitted. */
			// "noUnusedLocals": true,                           /* Enable error reporting when local variables aren't read. */
			// "noUnusedParameters": true,                       /* Raise an error when a function parameter isn't read. */
			// "exactOptionalPropertyTypes": true,               /* Interpret optional property types as written, rather than adding 'undefined'. */
			// "noImplicitReturns": true,                        /* Enable error reporting for codepaths that do not explicitly return in a function. */
			// "noFallthroughCasesInSwitch": true,               /* Enable error reporting for fallthrough cases in switch statements. */
			// "noUncheckedIndexedAccess": true,                 /* Add 'undefined' to a type when accessed using an index. */
			// "noImplicitOverride": true,                       /* Ensure overriding members in derived classes are marked with an override modifier. */
			// "noPropertyAccessFromIndexSignature": true,       /* Enforces using indexed accessors for keys declared using an indexed type. */
			// "allowUnusedLabels": true,                        /* Disable error reporting for unused labels. */
			// "allowUnreachableCode": true,                     /* Disable error reporting for unreachable code. */
			/* Completeness */
			// "skipDefaultLibCheck": true,                      /* Skip type checking .d.ts files that are included with TypeScript. */
			"skipLibCheck": true /* Skip type checking all .d.ts files. */
	}
}

2. package.json

javascript 复制代码
{
	"name": "demo2",
	"version": "1.0.0",
	"description": "",
	"main": "./src/index.ts",
	"scripts": {
		"build-cli": "browserify -p tsify ./src/index.ts > ./dist/index.js",
		"build-script": "node ./build.js > ./dist/index.js",
		"start": "lite-server"
	},
	"author": "",
	"license": "ISC",
	"dependencies": {
		"browserify": "^17.0.0",
		"lite-server": "^2.6.1",
		"tsify": "^5.0.4",
		"typescript": "^5.3.3"
	}
}

3. build.js

javascript 复制代码
var browserify = require('browserify');
var tsify = require('tsify');

browserify()
	.add('./src/index.ts')
	.plugin(tsify, { noImplicitAny: true })
	.bundle()
	.on('error', function (error) { console.error(error.toString()); })
	.pipe(process.stdout);

4. bs-config.js

javascript 复制代码
"use strict";
module.exports = {
	port: 8080,
	files: ['./dist/**/*.{html,css,js}'],
	server: {
		baseDir: './dist'
	}
}

5. index.html

html 复制代码
<!DOCTYPE html>
<html lang="en">

	<head>
		<meta charset="UTF-8">
		<link rel="icon" href="/favicon.ico">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<title>计算器演示</title>
		<style>
			html,
			body {
			width: 100%;
			height: 100%;
			margin: 0px;
			padding: 0px;
			}

			#app {
			display: flex;
			justify-content: center;
			justify-items: center;
			align-items: center;
			height: 100vh;
			}

			#app .panel {
			margin: 0 auto;
			width: 300px;
			height: 410px;
			border: 1px solid #f8f8f8;
			background-color: #f5f5f5;
			box-shadow: 0 0 4px 4px #d7d7d7b5;
			border-radius: 5px;
			}

			#app .panel .result {
			font-size: 30px;
			font-weight: bold;
			text-align: right;
			height: 60px;
			line-height: 60px;
			padding: 10px;
			padding-bottom: 0px;
			user-select: none;
			}

			#app .panel .buttons .line {
			display: flex;
			height: 55px;
			line-height: 55px;
			width: 100%;
			}

			#app .panel .buttons .line .btnPanel {
			padding: 5px;
			flex: 1;
			}

			#app .panel .buttons .line .btnPanel button {
			width: 100%;
			height: 100%;
			border-radius: 5px;
			border: 1px solid #eee;
			cursor: pointer;
			background-color: #fff;
			user-select: none;
			font-size: 18px;
			}

			#app .panel .buttons .line .btnPanel button:hover {
			background-color: #00adff;
			color: #fff;
			}

			#app .panel .buttons .line .btnPanel button.eq {
			background-color: #00adff;
			color: #fff;
			border: 1px solid #00adff;
			border-radius: 5px;
			}
		</style>
	</head>

	<body>
		<div id="app"></div>
		<script src="./index.js"></script>
		<script>

		</script>
	</body>

</html>

6. src/index.ts

javascript 复制代码
import ProcessFactory from './ProcessFactory';
import UI from './ui/UI';

const ui = new UI();


//初始化
ui.init(document.getElementById('app'), ProcessFactory);

7. src/ProcessFactory.ts

javascript 复制代码
import BackProcess from './proecess/BackProcess';
import BaseProcess from './proecess/BaseProcess';
import CProcess from './proecess/CProcess';
import EqProcess from './proecess/EqProcess';
import FenProcess from './proecess/FenProcess';
import NumberProcess from './proecess/NumerProcess';
import OpratorProcess from './proecess/OpratorProcess';
import PercentProcess from './proecess/PercentProcess';
import PinFangProcess from './proecess/PinFangProcess';
import PointProcess from './proecess/PointProcess';
import SqtProcess from './proecess/SqtProcess';

/**
 * 处理器的工厂函数,根据不同的字符,生成不同的处理器
 */
export default function getProcess(char: string): BaseProcess | null {
    if (char == '0' || char == '1' || char == '2' || char == '3' || char == '4' || char == '5' || char == '6' || char == '7' || char == '8' || char == '9') {
        return new NumberProcess(char);
    }
    if (char == '.') {
        return new PointProcess(char);
    }
    if (char == '=') {
        return new EqProcess(char);
    }
    if (char == '+' || char == '-' || char == '*' || char == '/') {
        return new OpratorProcess(char);
    }
    if (char == 'C') {
        return new CProcess(char);
    }
    if (char == '←') {
        return new BackProcess(char);
    }
    if (char == '%') {
        return new PercentProcess(char);
    }
    if (char == '1/x') {
        return new FenProcess(char);
    }
    if (char == 'x^2') {
        return new PinFangProcess(char);
    }
    if (char == '根号') {
        return new SqtProcess(char);
    }

    return null;
}

8. src/ui/UI.ts

javascript 复制代码
/**
 * 根容器
 */
const rootPanel: HTMLDivElement = document.createElement('div');

//展示结果
const resultPanel: HTMLDivElement = document.createElement('div');

//按钮容器
const buttonPanel: HTMLDivElement = document.createElement('div');

//按钮
const btns: string[][] = [
    ['%', 'CE', 'C', '←'],
    ['1/x', 'x^2', '根号', '/'],
    ['7', '8', '9', '*'],
    ['4', '5', '6', '-'],
    ['1', '2', '3', '+'],
    ['0', '.', '=']
];

//计算结果
let result = "0";

/**
 * UI工具
 */
export default class UI {
    /**
     * 初始化界面
     * @param root 
     */
    init(root: HTMLElement | null, getProcess: Function): HTMLDivElement {
        if (!root) {
            throw new Error('必须要指定根元素');
        }

        //设置类,控制样式
        rootPanel.className = 'panel';

        resultPanel.className = 'result';
        resultPanel.innerText = result;
        rootPanel.appendChild(resultPanel);

        buttonPanel.className = "buttons";

        btns.forEach(item => {
            let linePanel: HTMLDivElement = document.createElement('div');
            linePanel.className = 'line';
            item.forEach(text => {
                let buttonPanel: HTMLDivElement = document.createElement('div');
                buttonPanel.className = 'btnPanel';
                let button: HTMLButtonElement = document.createElement('button');
                button.innerText = text + "";
                if (text === '=') {
                    button.className = 'eq';
                }
                //附加按钮的标识,记录具体是什么内容
                button.setAttribute('content', text);
                let process = getProcess(text);
                if (process) {
                    button.onclick = () => {
                        result = process.process(result);
                        updateReslt();
                    };
                }
                buttonPanel.appendChild(button);
                linePanel.appendChild(buttonPanel);
            })
            buttonPanel.appendChild(linePanel);
        })

        rootPanel.appendChild(buttonPanel);


        //生成具体的元素
        root.appendChild(rootPanel);

        return rootPanel;
    }
}
/**
 * 更新计算结果
 */
function updateReslt() {
    resultPanel.innerText = result;
}

9. src/process/BaseProcess.ts

javascript 复制代码
/**
 * 处理器
 */
export default interface BaseProcess {
    /**
     * 要处理的字符串
     */
    char: string;
    /**
     * 按钮点击后计算结构
     * @param value  按钮的值
     * @param result 计算原始的结果
     */
    process(result: string): string;
}

10. src/process/BackProcess.ts

javascript 复制代码
import BaseProcess from './BaseProcess';

/**
 * 删除按钮的处理
 */
export default class BackProcess implements BaseProcess {
    char: string;
    constructor(char_: string) {
        this.char = char_;
    }
    /**
     *删除的处理
     * @param result 
     * @returns 
     */
    process(result: string): string {
        if (result.length > 0) {
            let result_ = result?.substring(0, result.length - 1);
            return result_ ? result_ : '0';
        }
        return '0';
    }
}

11. src/process/EqProcess.ts

javascript 复制代码
import BaseProcess from './BaseProcess';

/**
 * 等于号的处理
 */
export default class EqProcess implements BaseProcess {
    char: string;
    constructor(char_: string) {
        this.char = char_;
    }
    /**
     *清空的处理
     * @param result 
     * @returns 
     */
    process(result?: string): string {
        return '0';
    }
}

12. src/process/EqProcess.ts

javascript 复制代码
import BaseProcess from './BaseProcess';

/**
 * 等于号的处理
 */
export default class EqProcess implements BaseProcess {
    char: string;
    constructor(char_: string) {
        this.char = char_;
    }
    /**
     * 等于号的处理
     * @param value 空值
     * @param result 
     * @returns 
     */
    process(result: string): string {
        /**
         * 计算结果:1+2-3/4*5
         */
        while (result.indexOf('+') != -1 || result.indexOf('-') != -1 || result.indexOf('/') != -1 || result.indexOf('*') != -1) {
            //先计算乘除
            let chenIndex = result.indexOf('*');
            let chuIndex = result.indexOf('/');
            while (chenIndex >= 0 || chuIndex >= 0) {
                if (chenIndex >= 0 && chuIndex >= 0) {//有乘的计算、也有除的计算
                    if (chenIndex < chuIndex) {//乘在前
                        result = this.jisuan('*', result, chenIndex);
                    } else {
                        result = this.jisuan('/', result, chuIndex);
                    }
                } else {
                    if (chenIndex >= 0) {
                        result = this.jisuan('*', result, chenIndex);
                    } else if (chuIndex >= 0) {
                        result = this.jisuan('/', result, chuIndex);
                    }
                }
                chenIndex = result.indexOf('*');
                chuIndex = result.indexOf('/');
            }

            let jiaIndex = result.indexOf('+');
            let jianIndex = result.indexOf('-');
            if (jiaIndex >= 0 && jianIndex >= 0) {//计算加减
                if (jiaIndex < jianIndex) {
                    result = this.jisuan('+', result, jiaIndex);
                } else {
                    result = this.jisuan('-', result, jianIndex);
                }
            } else {
                if (jiaIndex >= 0) {
                    result = this.jisuan('+', result, jiaIndex);
                } else if (jianIndex >= 0) {
                    result = this.jisuan('-', result, jianIndex);
                }
            }
        }
        return result;
    }
    jisuan(op: string, result: string, index: number): string {
        let preStr = '';
        let startIndex = 0;
        for (let i = index - 1; i >= 0 && (result.charAt(i) !== '*' && result.charAt(i) !== '/' && result.charAt(i) !== '+' && result.charAt(i) !== '-'); i--) {
            preStr += result[i];
            startIndex = i;
        }
        //反转
        preStr = preStr.split('').reverse().join('');

        let nexStr = '';
        let endIndex = 0;
        for (let i = index + 1; i < result.length && (result.charAt(i) !== '*' && result.charAt(i) !== '/' && result.charAt(i) !== '+' && result.charAt(i) !== '-'); i++) {
            nexStr += result[i];
            endIndex = i;
        }
        let preNum = parseFloat(preStr);
        let nextNum = parseFloat(nexStr);

        let result_ = '';
        for (let i = 0; i < result.length; i++) {
            if (i >= startIndex && i <= endIndex) {
                if (i == startIndex) {
                    if (op == '*') {
                        result_ += (preNum * nextNum) + '';
                    } else if (op == '/') {
                        result_ += (preNum / nextNum) + '';
                    } else if (op == '+') {
                        result_ += (preNum + nextNum) + '';
                    } else {
                        result_ += (preNum - nextNum) + '';
                    }
                }
                continue;
            }
            result_ += result.charAt(i);
        }
        return result_;
    }
}

13. src/process/FenProcess.ts

javascript 复制代码
import BaseProcess from './BaseProcess';

/**
 * 几分之几的处理
 */
export default class FenProcess implements BaseProcess {
    char: string;
    constructor(char_: string) {
        this.char = char_;
    }
    /**
     * 几分之几的处理
     * @param value 空值
     * @param result 
     * @returns 
     */
    process(result: string): string {
        /**
         * 计算结果:1+2-3/4*5
         */
        while (result.indexOf('+') != -1 || result.indexOf('-') != -1 || result.indexOf('/') != -1 || result.indexOf('*') != -1) {
            //先计算乘除
            let chenIndex = result.indexOf('*');
            let chuIndex = result.indexOf('/');
            while (chenIndex >= 0 || chuIndex >= 0) {
                if (chenIndex >= 0 && chuIndex >= 0) {//有乘的计算、也有除的计算
                    if (chenIndex < chuIndex) {//乘在前
                        result = this.jisuan('*', result, chenIndex);
                    } else {
                        result = this.jisuan('/', result, chuIndex);
                    }
                } else {
                    if (chenIndex >= 0) {
                        result = this.jisuan('*', result, chenIndex);
                    } else if (chuIndex >= 0) {
                        result = this.jisuan('/', result, chuIndex);
                    }
                }
                chenIndex = result.indexOf('*');
                chuIndex = result.indexOf('/');
            }

            let jiaIndex = result.indexOf('+');
            let jianIndex = result.indexOf('-');
            if (jiaIndex >= 0 && jianIndex >= 0) {//计算加减
                if (jiaIndex < jianIndex) {
                    result = this.jisuan('+', result, jiaIndex);
                } else {
                    result = this.jisuan('-', result, jianIndex);
                }
            } else {
                if (jiaIndex >= 0) {
                    result = this.jisuan('+', result, jiaIndex);
                } else if (jianIndex >= 0) {
                    result = this.jisuan('-', result, jianIndex);
                }
            }
        }
        return (1 / parseFloat(result)) + '';
    }
    jisuan(op: string, result: string, index: number): string {
        let preStr = '';
        let startIndex = 0;
        for (let i = index - 1; i >= 0 && (result.charAt(i) !== '*' && result.charAt(i) !== '/' && result.charAt(i) !== '+' && result.charAt(i) !== '-'); i--) {
            preStr += result[i];
            startIndex = i;
        }
        //反转
        preStr = preStr.split('').reverse().join('');

        let nexStr = '';
        let endIndex = 0;
        for (let i = index + 1; i < result.length && (result.charAt(i) !== '*' && result.charAt(i) !== '/' && result.charAt(i) !== '+' && result.charAt(i) !== '-'); i++) {
            nexStr += result[i];
            endIndex = i;
        }
        let preNum = parseFloat(preStr);
        let nextNum = parseFloat(nexStr);

        let result_ = '';
        for (let i = 0; i < result.length; i++) {
            if (i >= startIndex && i <= endIndex) {
                if (i == startIndex) {
                    if (op == '*') {
                        result_ += (preNum * nextNum) + '';
                    } else if (op == '/') {
                        result_ += (preNum / nextNum) + '';
                    } else if (op == '+') {
                        result_ += (preNum + nextNum) + '';
                    } else {
                        result_ += (preNum - nextNum) + '';
                    }
                }
                continue;
            }
            result_ += result.charAt(i);
        }
        return result_;
    }
}

14. src/process/NumerProcess.ts

javascript 复制代码
import BaseProcess from './BaseProcess';

/**
 * 计算数字型的按钮
 */
export default class NumberProcess implements BaseProcess {
    char: string;
    constructor(char_: string) {
        this.char = char_;
    }
    /**
     * 计算结果,传入的是数字按钮
     * @param value 数字
     * @param result 现有的结果
     * @returns 合并后的值
     */
    process(result: string): string {
        if (this.char == '0') {
            if (parseFloat(result) == 0) {
                return '0';
            }
        }
        if (parseFloat(result) == 0 && result.indexOf('.') == -1) {
            return this.char;
        } else {
            return result + '' + this.char;
        }
    }
}

15. src/process/OpratorProcess.ts

javascript 复制代码
import BaseProcess from './BaseProcess';

/**
 * 实现操作符的处理
 */
export default class OpratorProcess implements BaseProcess {
    char: string;
    constructor(char_: string) {
        this.char = char_;
    }
    /**
     * 操作符的处理,+,-,*,/
     * @param result 
     * @returns 合并后的结果
     */
    process(result: string): string {
        if (result.charAt(result.length - 1) == '+' || result.charAt(result.length - 1) == '-' || result.charAt(result.length - 1) == '*' || result.charAt(result.length - 1) == '/') {
            return result;
        }
        return '' + result + this.char;
    }
}

16. src/process/PercentProcess.ts

javascript 复制代码
import BaseProcess from './BaseProcess';

/**
 * 百分号的处理
 */
export default class PercentProcess implements BaseProcess {
    char: string;
    constructor(char_: string) {
        this.char = char_;
    }
    /**
     * 百分号的处理
     * @param value 空值
     * @param result 
     * @returns 
     */
    process(result: string): string {
        /**
         * 计算结果:1+2-3/4*5
         */
        while (result.indexOf('+') != -1 || result.indexOf('-') != -1 || result.indexOf('/') != -1 || result.indexOf('*') != -1) {
            //先计算乘除
            let chenIndex = result.indexOf('*');
            let chuIndex = result.indexOf('/');
            while (chenIndex >= 0 || chuIndex >= 0) {
                if (chenIndex >= 0 && chuIndex >= 0) {//有乘的计算、也有除的计算
                    if (chenIndex < chuIndex) {//乘在前
                        result = this.jisuan('*', result, chenIndex);
                    } else {
                        result = this.jisuan('/', result, chuIndex);
                    }
                } else {
                    if (chenIndex >= 0) {
                        result = this.jisuan('*', result, chenIndex);
                    } else if (chuIndex >= 0) {
                        result = this.jisuan('/', result, chuIndex);
                    }
                }
                chenIndex = result.indexOf('*');
                chuIndex = result.indexOf('/');
            }

            let jiaIndex = result.indexOf('+');
            let jianIndex = result.indexOf('-');
            if (jiaIndex >= 0 && jianIndex >= 0) {//计算加减
                if (jiaIndex < jianIndex) {
                    result = this.jisuan('+', result, jiaIndex);
                } else {
                    result = this.jisuan('-', result, jianIndex);
                }
            } else {
                if (jiaIndex >= 0) {
                    result = this.jisuan('+', result, jiaIndex);
                } else if (jianIndex >= 0) {
                    result = this.jisuan('-', result, jianIndex);
                }
            }
        }
        return (parseFloat(result) / 100) + '';
    }
    jisuan(op: string, result: string, index: number): string {
        let preStr = '';
        let startIndex = 0;
        for (let i = index - 1; i >= 0 && (result.charAt(i) !== '*' && result.charAt(i) !== '/' && result.charAt(i) !== '+' && result.charAt(i) !== '-'); i--) {
            preStr += result[i];
            startIndex = i;
        }
        //反转
        preStr = preStr.split('').reverse().join('');

        let nexStr = '';
        let endIndex = 0;
        for (let i = index + 1; i < result.length && (result.charAt(i) !== '*' && result.charAt(i) !== '/' && result.charAt(i) !== '+' && result.charAt(i) !== '-'); i++) {
            nexStr += result[i];
            endIndex = i;
        }
        let preNum = parseFloat(preStr);
        let nextNum = parseFloat(nexStr);

        let result_ = '';
        for (let i = 0; i < result.length; i++) {
            if (i >= startIndex && i <= endIndex) {
                if (i == startIndex) {
                    if (op == '*') {
                        result_ += (preNum * nextNum) + '';
                    } else if (op == '/') {
                        result_ += (preNum / nextNum) + '';
                    } else if (op == '+') {
                        result_ += (preNum + nextNum) + '';
                    } else {
                        result_ += (preNum - nextNum) + '';
                    }
                }
                continue;
            }
            result_ += result.charAt(i);
        }
        return result_;
    }
}

17. src/process/PinFangProcess.ts

javascript 复制代码
import BaseProcess from './BaseProcess';

/**
 * 几分之几的处理
 */
export default class PinFangProcess implements BaseProcess {
    char: string;
    constructor(char_: string) {
        this.char = char_;
    }
    /**
     * 几分之几的处理
     * @param value 空值
     * @param result 
     * @returns 
     */
    process(result: string): string {
        /**
         * 计算结果:1+2-3/4*5
         */
        while (result.indexOf('+') != -1 || result.indexOf('-') != -1 || result.indexOf('/') != -1 || result.indexOf('*') != -1) {
            //先计算乘除
            let chenIndex = result.indexOf('*');
            let chuIndex = result.indexOf('/');
            while (chenIndex >= 0 || chuIndex >= 0) {
                if (chenIndex >= 0 && chuIndex >= 0) {//有乘的计算、也有除的计算
                    if (chenIndex < chuIndex) {//乘在前
                        result = this.jisuan('*', result, chenIndex);
                    } else {
                        result = this.jisuan('/', result, chuIndex);
                    }
                } else {
                    if (chenIndex >= 0) {
                        result = this.jisuan('*', result, chenIndex);
                    } else if (chuIndex >= 0) {
                        result = this.jisuan('/', result, chuIndex);
                    }
                }
                chenIndex = result.indexOf('*');
                chuIndex = result.indexOf('/');
            }

            let jiaIndex = result.indexOf('+');
            let jianIndex = result.indexOf('-');
            if (jiaIndex >= 0 && jianIndex >= 0) {//计算加减
                if (jiaIndex < jianIndex) {
                    result = this.jisuan('+', result, jiaIndex);
                } else {
                    result = this.jisuan('-', result, jianIndex);
                }
            } else {
                if (jiaIndex >= 0) {
                    result = this.jisuan('+', result, jiaIndex);
                } else if (jianIndex >= 0) {
                    result = this.jisuan('-', result, jianIndex);
                }
            }
        }
        return (parseFloat(result) * parseFloat(result)) + '';
    }
    jisuan(op: string, result: string, index: number): string {
        let preStr = '';
        let startIndex = 0;
        for (let i = index - 1; i >= 0 && (result.charAt(i) !== '*' && result.charAt(i) !== '/' && result.charAt(i) !== '+' && result.charAt(i) !== '-'); i--) {
            preStr += result[i];
            startIndex = i;
        }
        //反转
        preStr = preStr.split('').reverse().join('');

        let nexStr = '';
        let endIndex = 0;
        for (let i = index + 1; i < result.length && (result.charAt(i) !== '*' && result.charAt(i) !== '/' && result.charAt(i) !== '+' && result.charAt(i) !== '-'); i++) {
            nexStr += result[i];
            endIndex = i;
        }
        let preNum = parseFloat(preStr);
        let nextNum = parseFloat(nexStr);

        let result_ = '';
        for (let i = 0; i < result.length; i++) {
            if (i >= startIndex && i <= endIndex) {
                if (i == startIndex) {
                    if (op == '*') {
                        result_ += (preNum * nextNum) + '';
                    } else if (op == '/') {
                        result_ += (preNum / nextNum) + '';
                    } else if (op == '+') {
                        result_ += (preNum + nextNum) + '';
                    } else {
                        result_ += (preNum - nextNum) + '';
                    }
                }
                continue;
            }
            result_ += result.charAt(i);
        }
        return result_;
    }
}

18. src/process/PointProcess.ts

javascript 复制代码
import BaseProcess from './BaseProcess';

/**
 * 实现操作符的处理
 */
export default class OpratorProcess implements BaseProcess {
    char: string;
    constructor(char_: string) {
        this.char = char_;
    }
    /**
     * .的处理
     * @param result 
     * @returns 合并后的结果
     */
    process(result: string): string {
        if (result.charAt(result.length - 1) == '.') {
            return result;
        }
        if (result.charAt(result.length - 1) == '+' || result.charAt(result.length - 1) == '-' || result.charAt(result.length - 1) == '*' || result.charAt(result.length - 1) == '/') {
            return result + '0.';
        }
        return result + '' + this.char;
    }
}

19. src/process/SqtProcess.ts

javascript 复制代码
import BaseProcess from './BaseProcess';

/**
 * 几分之几的处理
 */
export default class SqtProcess implements BaseProcess {
    char: string;
    constructor(char_: string) {
        this.char = char_;
    }
    /**
     * 几分之几的处理
     * @param value 空值
     * @param result 
     * @returns 
     */
    process(result: string): string {
        /**
         * 计算结果:1+2-3/4*5
         */
        while (result.indexOf('+') != -1 || result.indexOf('-') != -1 || result.indexOf('/') != -1 || result.indexOf('*') != -1) {
            //先计算乘除
            let chenIndex = result.indexOf('*');
            let chuIndex = result.indexOf('/');
            while (chenIndex >= 0 || chuIndex >= 0) {
                if (chenIndex >= 0 && chuIndex >= 0) {//有乘的计算、也有除的计算
                    if (chenIndex < chuIndex) {//乘在前
                        result = this.jisuan('*', result, chenIndex);
                    } else {
                        result = this.jisuan('/', result, chuIndex);
                    }
                } else {
                    if (chenIndex >= 0) {
                        result = this.jisuan('*', result, chenIndex);
                    } else if (chuIndex >= 0) {
                        result = this.jisuan('/', result, chuIndex);
                    }
                }
                chenIndex = result.indexOf('*');
                chuIndex = result.indexOf('/');
            }

            let jiaIndex = result.indexOf('+');
            let jianIndex = result.indexOf('-');
            if (jiaIndex >= 0 && jianIndex >= 0) {//计算加减
                if (jiaIndex < jianIndex) {
                    result = this.jisuan('+', result, jiaIndex);
                } else {
                    result = this.jisuan('-', result, jianIndex);
                }
            } else {
                if (jiaIndex >= 0) {
                    result = this.jisuan('+', result, jiaIndex);
                } else if (jianIndex >= 0) {
                    result = this.jisuan('-', result, jianIndex);
                }
            }
        }
        return Math.sqrt(parseFloat(result)) + '';
    }
    jisuan(op: string, result: string, index: number): string {
        let preStr = '';
        let startIndex = 0;
        for (let i = index - 1; i >= 0 && (result.charAt(i) !== '*' && result.charAt(i) !== '/' && result.charAt(i) !== '+' && result.charAt(i) !== '-'); i--) {
            preStr += result[i];
            startIndex = i;
        }
        //反转
        preStr = preStr.split('').reverse().join('');

        let nexStr = '';
        let endIndex = 0;
        for (let i = index + 1; i < result.length && (result.charAt(i) !== '*' && result.charAt(i) !== '/' && result.charAt(i) !== '+' && result.charAt(i) !== '-'); i++) {
            nexStr += result[i];
            endIndex = i;
        }
        let preNum = parseFloat(preStr);
        let nextNum = parseFloat(nexStr);

        let result_ = '';
        for (let i = 0; i < result.length; i++) {
            if (i >= startIndex && i <= endIndex) {
                if (i == startIndex) {
                    if (op == '*') {
                        result_ += (preNum * nextNum) + '';
                    } else if (op == '/') {
                        result_ += (preNum / nextNum) + '';
                    } else if (op == '+') {
                        result_ += (preNum + nextNum) + '';
                    } else {
                        result_ += (preNum - nextNum) + '';
                    }
                }
                continue;
            }
            result_ += result.charAt(i);
        }
        return result_;
    }
}

六.遇到的问题

问题一: HTMLDivElement无法继承的问题。

因为ts中HTMLDivElement是一个接口,没有声明类,因为是一个底层类型,不适合进行扩展。

可以新建类实现一个此接口,但就需要实现所有的属性和接口的方法,不太显示;

可以新建一个类,定义一个HTMLDivElement类型的数据,将需要封装的内容封装到新建的类中实现想要的效果;

也可使用声明合并对HTMLDivElement进行扩展声明,实现相应的功能。
问题二: 直接给元素添加点击事件回调时,接口类型的问题。

相关推荐
Watermelo6177 分钟前
详解js柯里化原理及用法,探究柯里化在Redux Selector 的场景模拟、构建复杂的数据流管道、优化深度嵌套函数中的精妙应用
开发语言·前端·javascript·算法·数据挖掘·数据分析·ecmascript
m0_748248949 分钟前
HTML5系列(11)-- Web 无障碍开发指南
前端·html·html5
m0_7482356120 分钟前
从零开始学前端之HTML(三)
前端·html
一个处女座的程序猿O(∩_∩)O2 小时前
小型 Vue 项目,该不该用 Pinia 、Vuex呢?
前端·javascript·vue.js
hackeroink5 小时前
【2024版】最新推荐好用的XSS漏洞扫描利用工具_xss扫描工具
前端·xss
迷雾漫步者7 小时前
Flutter组件————FloatingActionButton
前端·flutter·dart
向前看-7 小时前
验证码机制
前端·后端
燃先生._.8 小时前
Day-03 Vue(生命周期、生命周期钩子八个函数、工程化开发和脚手架、组件化开发、根组件、局部注册和全局注册的步骤)
前端·javascript·vue.js
高山我梦口香糖9 小时前
[react]searchParams转普通对象
开发语言·前端·javascript