JavaScript - 贷款计算器

前言

在JavaScript权威指南一书中,有贷款计算器一例。
书中有很多低级错误(我应该是盗版…),经过一些修改,它成功完成了贷款计算,我把源码放到这里,记录一下。

HTML代码


<html>
<head>
<meta charset="UTF-8">
<title>Javascript Loan Calculatortitle>
<style>
*{padding:0;margin:0;}
.output{font-weight:bold;}
#payment{text-decoration:underline;}
#graph{border:solid black 1px;}
th,td{vertical-align:top;}
style>
head>
<body>
    
<script src="js.js">script>
    
<table>

    <tr>
        <th>输入贷款数据:th>
        <td>td>
        <th>贷款余额,累积权益和利息支付th>
    tr>

    <tr>
       <td>贷款金额($):td>
       <td><input id="amount" onchange="calculator();">td>
       <td rowspan=8>
           <canvas id="graph" width="400" height="250">canvas>
       td>
    tr>
        
    <tr>
        <td>年利率(%):td>
        <td><input id="apr" onchange="calculator();">td>
    tr>

    <tr>
        <td>还款期(年):td>
        <td><input id="years" onchange="calculator();">td>

    <tr>
        <td>Zipcode(寻找贷方):td>
        <td><input id="zipcode" onchange="calculator();">td>

    <tr>
        <th>近似付款:th>
        <td>

JavaScript代码

// 2019.6.19 Javascript Loan Calculator
// 如果浏览器支持则开启ECMAScript 5(ES5)严格模式
"use strict";

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/* 【函数功能】
A. 封装calculate()函数,在html代码中绑定事件处理程序时会调用它
B. 此函数从读取数据,计算贷款赔付信息,并将结果显示在
C. 它还保存了用户数据、展示了放贷人链接并绘制出了图标
*/

function calculator(){
    // 查找文档中用于输入输出的元素
    // 这些amount|apr|变量都是局部的,函数运行结束后立刻销毁

    // amount|apr|years|zipcode => input
    var amount = document.getElementById("amount");
    var apr = document.getElementById("apr");
    var years = document.getElementById("years");
    var zipcode = document.getElementById("zipcode");

    // payment|total|totalinterest => output
    var payment = document.getElementById("payment");
    var total = document.getElementById("total");
    var totalinterest = document.getElementById("totalinterest");

    // 假设所有输入都是合法的,将从input元素中获取输入数据
    // 将百分比格式转换为小数格式,并从年利率转换为月利率
    // 将年度赔付转换为月度赔付
    var principal = parseFloat(amount.value);
    var interest = parseFloat(apr.value)/100/12;
    var payments = parseFloat(years.value)*12;

    // 计算月度赔付的数据
    var x = Math.pow(1+interest,payments); //进行幂次运算
    var monthly = (principal * x * interest) / (x-1);

    // 如果结果没有超过JavaScript能表示的数字范围,且用户的输入也正正确
    // 这里所展示的结果就是合法有效的
    if(isFinite(monthly)){ // isFinite检测monthly是否无穷大
        // 将数据填充到并四舍五入到小数点后2位
        payment.innerHTML = monthly.toFixed(2);
        total.innerHTML = (monthly*payments).toFixed(2);
        totalinterest.innerHTML = ((monthly*payments)-principal).toFixed(2);

        // 将用户输入的数据保存下来(方便下次也能取到历史数据)
        save(amount.value,apr.value,years.value,zipcode.value);

        // 找到并展示本地放贷人(忽略网络错误)
        try{ // 捕获这段代码抛出的所有异常
            getLenders(amount.value,apr.value,years.value,zipcode.value);
        }catch(e){
            /* 忽略抛出这些异常 */ 
        }

        // 图表展示贷款余额、利息、资产收益
        chart(principal,interest,monthly,payments);
    }else{
        // 计算结果不是数字或是无穷大(表示数据是非法或不完整)
        // 清空之前的输出数据
        payment.innerHTML = "";
        total.innerHTML = "";
        totalinterest.innerHTML = "";
        chart(); // 如不传参则清空图表
    }
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/* 【函数功能】
A. 将用户的输入保存至localStorage对象属性中
B. 这些属性在再次访问时还会继续保持在原位置
C. 如果在浏览器中使用file://URL方式打开则无法使用浏览器存储功能(fireFox)
D. 通过HTPP是可以实现的
*/

function save(amount,apr,years,zipcode){
    // 只有浏览器支持时才执行(window.localStorage判断)
    if(window.localStorage){
        localStorage.loan_amount = amount;
        localStorage.loan_apr = apr;
        localStorage.loan_years = years;
        localStorage.loan_zipcode = zipcode;
    }
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/* 【全局onload函数】
A. 在文档首次加载时尝试还原输入字段
*/

window.onload = function(){
    // 如果浏览器支持本地存储并且上次保存的值是存在的
    if(window.localStorage && localStorage.loan_amount){
        document.getElementById("amount").value = localStorage.loan_amount;
        document.getElementById("apr").value = localStorage.loan_apr;
        document.getElementById("years").value = localStorage.loan_years;
        document.getElementById("zipcode").value = localStorage.loan_zipcode;
    }
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/* 【函数功能】
A. 将用户的输入发送至服务器端脚本(理论上)
B. 将返回一个本地放贷人的链接列表(未实现)
C. 但如果该服务存在 则getLenders()有效
*/

function getLenders(amount,apr,years,zipcode){
    // 如果浏览器不支持XMLHttpRequest对象(则退出)
    if(!window.XMLHttpRequest) return;

    // 找到要显示的放贷人列表的元素
    var ad = document.getElementById("lenders");
    if(!ad) return; // 没有则退出

    // 将用户的输入数据进行URL编码 并作为查询参数附加在URL里
    var url = "getLenders.php"+            // 处理数据的URL地址
    "?amt=" + encodeURIComponent(amount)+  // 使用查询串中的数据 
    "&apr=" + encodeURIComponent(apr)+
    "&yrs=" + encodeURIComponent(years)+
    "&zip=" + encodeURIComponent(zipcode);

    // 通过XMLHttpRequest对象来提取返回数据
    var req = new XMLHttpRequest(); // 发起一个新的请求
    req.open("GET",url); // 通过URL发起一个HTTP GET请求
    req.send(null); // 不带任何正文发送这个请求

    // 在返回数据之前 注册了一个事件处理函数
    // 这个事件处理函数将会在服务器的响应返回至客户端的时候调用
    req.onreadystatechange = function(){
        if(req.readyState == 4 && req.status == 200){
            // 如果代码运行到这里 说明得到了一个合法且完整的HTTP响应
            var response = req.responseText; // HTTP响应是以字符串的形式呈现的
            var lenders = JSON.parse(response); // 将其解析为JS数组
    
            // 将数组中的放贷人对象转换为HTML字符串形式
            var list = "";
            for(var i=0;i<lenders.length;i++){
                list += "
  • "+lenders[i].name+""; } // 将数据在HTML元素中呈现出来 ad.innerHTML = "
      " + list + "
    "
    ; } } } // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ /* 【函数功能】 A. 在HTML元素中用图表展示月度贷款余额、利息、资产收益 B. 如果不传入参数的话则清空之前的图表数据 */ function chart(principal,interest,monthly,payments){ var graph = document.getElementById("graph"); // 得到标签 g.clearRect(0, 0, width, height); // 清除并重置画布 // 如果不传入参数|或者浏览器不支持画布则直接返回 if(arguments.length == 0 || lgraph.getContext) return; // 获得画布元素的"context"对象 这个对象定义了一组绘画API var g = graph.getContext("2d"); // 所有绘画操作都基于这个对象 var width = graph.width; height = graph.height; // 获得画布的大小 // 这里的函数作用是将付款数字和美元数据转换为像素 function paymentToX(n){ return n * width / payments; } function amountToY(a){ return height - (a*height/(monthly*payments*1.05)); } // 付款数据是一条从(0,0)到(payments,monthly*payments)的直线 g.moveTo(paymentToX(0),amountToY(0)); //从左下方开始 g.lineTo(paymentToX(payments),amountToY(monthly*payments)); //绘制右上方 g.lineTo(paymentToX(payments),amountToY(0)); //再至右下方 g.closePath(); //将结尾连接至开头 g.fillStyle = "#f88"; //亮红色 g.fill(); //填充矩形 g.font = "bold 12px sans-serif"; //定义一种字体 g.fillText("Total Interest Payments",20,20); //将文字绘制到图例中 // 很多资产数据并不是线性 很难反映到图表中 var equity = 0; g.beginPath(); //开始绘制新图形 g.moveTo(paymentToX(0),amountToY(0)); //从左下方开始 for(var p=1;p<=payments;p++){ // 计算每一笔赔付利息 var thisMonthsInterest = (principal - equity) * interest; equity += (monthly - thisMonthsInterest); //得到资产额 g.lineTo(paymentToX(p),amountToY(equity)); //将数据绘制到画布中 } g.lineTo(paymentToX(payments),amountToY(0)); //将数据线绘制至X轴 g.closePath(); //将线条结尾连接至线条开头 g.fillStyle = "green"; //使用绿色绘制图形 g.fill(); //曲线之下的部分均填充 g.fillText("Total Equity",20,35); //文本颜色设置为绿色 // 再次循环 余额数据显示为黑色粗线条 var bal = principal; g.beginPath(); g.moveTo(paymentToX(0),amountToY(bal)); for(var p=1;p<=payments;p++){ var thisMonthsInterest = bal * interest; bal -= (monthly - thisMonthsInterest); //得到资产额 g.lineTo(paymentToX(p),amountToY(bal)); //将直线连接至某点 } g.lineWidth = 3; //将直线宽度加粗 g.stroke(); //绘制余额的曲线 g.fillStyle = "black"; //使用黑色字体 g.fillText("Loan Balance",20,50); //图例文字 // 将年度数据在X轴做标记 g.textAlign = "center"; //文字居中对齐 var y = amountToY(0); //Y坐标设为0 for(var year=1;year*12 <= payments;year++){ //遍历每年 var x = paymentToX(year * 12); //计算标记位置 g.fillRect(x-0.5,y-3,1,3); //开始绘制标记 if(year == 1) g.fillText("Year",x,y-5); //在坐标轴做标记 if(year % 5 == 0 && year *12 !== payments) //每5年的数据 g.fillText(String(year),x,y-5); } // 将赔付数额标记在右边界 g.textAlign = "right"; //文字右对齐 g.textBaseline = "middle"; //文字垂直居中 var ticks = [monthly*payments,principal]; //将用到的两个点 var rightEdge = paymentToX(payments); //设置X坐标 for(var i=0;i<ticks.length;i++){ //对每两个点做循环 var y = amountToY(ticks[i]); //计算每个标记的Y坐标 g.fillRect(rightEdge-3,y-0.5,3,1); //绘制标记 g.fillText(String(ticks[i].toFixed(0)),rightEdge-5,y); } } // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  • JavaScript - 贷款计算器_第1张图片

    你可能感兴趣的:(+,JScript)